今天来介绍一下文件的操作,包括文件的增删改查,以及字符编码与转码,还有一些简单的函数操作。
要对文件进行操作,首先要打开一个文件,而打开文件需要声明要对这个文件干什么,比如读(‘r‘),写(‘w‘),追加(‘a‘),读写(‘r+‘),写读(‘w+‘),还需要告诉系统,用什么字符集来打开,如utf-8或者Unicode等
首先介绍一下文件的读取。 f.read 这个是读取整个文件的内容。
首先有个叫‘yesterday‘的文本文件(就当他有,我这里没写这个文件) f=open(‘yesterday‘,‘r‘,encoding=‘utf-8‘)data1=f.readdata2=f.readprint(data1,“1”)print(data2,“2”)f.close
你会发现,输出的结果是data1输出的是文件的内容,data2输出的是空,而不是输出了两次整个文件,也不是读了两行。因为在这里,要介绍一个句柄的东西,大家可以理解成光标,f.read在读完文件的时候,句柄就停留在文件最后的位置,
在执行data2的时候,句柄在最后的位置,再往后面是没有内容的,所以输出的是一个空。而不是读第二遍文件。
这里还要注意一点的是,文件无论进行什么 操作,只要打开了文件,后面一定要关闭,f.close。
有时候我们可能不需要直接读取整个文件,这里介绍一下按行读取 f.readline
这里介绍几种方法
f=open(‘yesterday‘,‘r‘,encoding=‘utf-8‘)我们可以从第一行读几行for i in range (5): print(f.readline())也可以读完整个文件
for line in f.readlines(): print(line.strip()) 这里还有一种操作是按行输出,并写出这是第几行 用的是enumeratefor index,line in enumerate(f.readlines()): print(index,line) 不过,readline命令是全读到内存里,运行速度不如在内存中读一行,删一行的速度快for line in f : print(line)
文件的读取介绍的差不多了,下面介绍文件的写入,‘w’模式是写入模式,他是创建(如果文件存在就覆盖)一个文件。
f=open(‘yesterday‘,‘w‘,encoding=‘utf-8‘)f.write("abc")f.weite("456")f.close
代码执行之后会看到yesterday文件中有一行数据 abc456 ,他没有向我们程序中那样,写成两行,也是因为上文中提到的句柄,因为在写完abc之后,句柄就停留在了abc的后面,没有自动换行,所以在写入456的时候,他会紧接着abc写。
这里还有一点,就是在文件的写入过程中,不是write代码执行完成后,就立即写入硬盘文件中,而是在关闭这个文件的时候,数据才会一起写入磁盘中,之前都是保存在内存中的,所以这里也必须记得关闭文件。
如果想执行完write 就立即写入的话,可以再write语句后面使用 f.flush() 这个是直接写入到文件中,不需要关闭文件,括号中如果填数字,代表写这么多字符后自动写入文件。
还有几种文件的打开模式
A. “a” 在这个模式下,写入的数据都会追加在原文件的末尾,而不会覆盖原文件。
B. “r+”这个是读写模式,所谓读写模式是先读再写的模式。当你读取了三行后再写入,新写入的文件会出现在第三行的末尾而不是第四行。因为读取结束时,文件的句柄停留在了第三行的结尾,而且这个句柄不会自动移动到第四行,所以新写的数据会出现在第三行结束。读写模式,可读可写可追加,平时用处比较多。
C. “w+”这个是写读模式,在这个模式下,系统会自动去寻找有没有这个文件,如果没有的话直接新建一个文件,如果有的话,覆盖这个文件。在读写模式下,先读取文件是没有用的,因为文件被覆盖,读不到任何数据。在写入的时候,虽然可以移动句柄,但是无法在移动后的句柄处写入,还是会写入在文件的末尾处。
D. "a+"是追加读写
E. "rb"让文件以二进制方式读取出来。
F. “wb”以二进制方式写入。这时候就不能输入英文,汉字等了,写入的时候会报错,因为输入的不是二进制。
这里在介绍一下文件的修改。文件的修改有两种方式。
1.把文件全读取到内存中,修改完成后再全部写入。这种方法不推荐用,因为当文件过大时,内存可能无法处理这个文件,而且就算是一般的文件,处理速度也会比较慢,因为占用内存比较大。
2.打开原文件的同时打开另一个文件,将修改完成的文件写入另一个新的文件,这样可以一部分一部分的去执行,提高运行速度,节省内存,是主流的修改文件的方法。
修改的语句可以用replace
for line in f : if "abc" in line : line = line.repiace("abc","def") 这就实现了一个,将文件中的abc替换成def的功能。
打开文件还有另一种操作,就是用with,用with的好处就是不用去关闭文件,他会自动帮你处理好
with open ("yesterday","r",encoding="utf-8") as f : print (f.readline()) 打开多个文件的方法是 with open ("yesterday1","r",encoding="utf-8") as f , with open ("yesterday2","r",encoding="utf-8") as ff : 下一个文件要记缩进。
下面介绍字符编码和转码
python3中默认的编码是Unicode。在py3中encode,在转码的同时还会把string 变成bytes类型,decode在解码的同时还会把bytes变回string。
不管是encode还是decode。只要记住在不同的字符集转换的时候,都要经过Unicode再转到别的字符集!!!
utf-8是Unicode的扩展集,所以Unicode可以在utf-8下直接打印出来。
最后介绍一部分函数。
所以,先定义一个函数
def test (x) : x+=1 return 0
这样,我们就定义了一个名为test的函数,他的形参是x,他的函数体是x+=1, 返回值是一个0。如果未在函数中指定return,那这个函数的返回值为None
函数和过程最大的区别是函数必须有返回值,而过程是没有返回值的。
return 后面可以跟任何东西,他代表函数执行完成的一个标志,返回这个值就相当于说函数已经执行完成,而在函数中,return后面的语句也不会被执行,因为return是这个函数的结尾。
假设一个 a= test(1) 打印他会得到0.这就是上个函数执行完成的标志 。也可以return x 。这个会打印出一个2.因为函数中有x+=1.执行完成后x就变成了2.
形参和实参:
形参:变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量
实参:可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值
就像刚才上边的例子 x 就是一个形参。而a=test(1)中的1就是实参。
一个函数可以有多个形参和实参,比如下面代码
def test(x,y): print (x) print (y) return 0 test(1,2) test(x=1,y=2) test(1,y=2) 以上三种调用函数的传参方法都对,前两种不必多说,第三种方法,在函数中,他会主动识别位置参数,然后在去看赋值。但是不能将赋值卸载位置参数前面,会报错
函数还能赋默认参数
def test(x,y=1) print(x) print(y) return 0 test(1) test(1,3) test(1,y=3) test(x=1) test(x=1,y=3) 以上方法都正确,默认参数是可以赋也可以不赋值的。这种多用于有默认选项的情况,如默认国籍,或者默认数据库服务器端口等
函数也可以通过元祖,字典等方式传值
def test (*args) print(args) return 0 test(1,2,3,4) test(*[1,2,3,4]) 这两种也都可以的 这种传值方法多用于与传单一参数一起传递值得情况,比如一开始我定义了一个函数,只有一个形参x ,但是后来发现形参不够,还想加,就可以用下面的方法,省的日后再去改代码 def test (x,*args) print (x) print (args) return 0 这时候可以用test(1,2,3,4)传值 输出的结果为 1 (2,3,4)
通过字典传值
def stu_register(name,age,*args,**kwargs): # *kwargs 会把多传入的参数变成一个dict形式 print(name,age,args,kwargs) return 0 stu_register("Alex",22) #输出 #Alex 22 () {}#后面这个{}就是kwargs,只是因为没传值,所以为空 stu_register("Jack",32,"CN","Python",sex="Male",province="ShanDong") #输出 # Jack 32 (‘CN‘, ‘Python‘) {‘province‘: ‘ShanDong‘, ‘sex‘: ‘Male‘}
既然降到了函数,那么必然少不了局部变量和全局变量
在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。
当全局变量与局部变量同名时:
在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用
x=1 def aaa(x,y) print(x) x=3 print(x) c=x+y return 0 aaa(x,1) print(x)
这个函数执行完之后是
1
3
1
在程序最顶端的是全局变量
而在函数里面的x是局部变量
虽然在函数里面可以通过语句global改变全局变量,但是不建议使用。
而且,只有str ,int 不能再函数中改变局部变量。
但是,列表,元祖,字典,集合,类,都可以在局部变量中改变全局变量。
递归函数
在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。
def calc(n): print(n) if int(n/2) ==0: return n return calc(int(n/2)) calc(10) 输出: 10 5 2 1
递归特性:
1. 必须有一个明确的结束条件
2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
3. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
高阶函数
变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
注:abs是取绝对值
def add(x,y,f): return f(x) + f(y) res = add(3,-6,abs) print(res)