第三章 Python基础——文件操作&函数
3.1三元运算
三元运算又称三目运算,是对简单条件的简写
if 条件成立: val=1 else: val=2
改成三元运算:
val=1 if条件成立 else 2
3.2字符编码转换
编码的种类情况:
ASCLL占1个字符,只支持英文
GB2312占2个字节,支持6700+汉字
GBK GB2312的升级版,支持21000+汉字
Shift-JIS日本字符
ks_c_5601-1987韩国编码
TIS-620泰国编码
万国码:Unicode
两个作用:1.支持全球的所有语言,每个国家都可以用Unicode。
2.unicode包含了跟全球所有国家编码的映射关系。
总之:UTF是为Unicode编码设计的一种在储存和传输时节省空间的编码方案
注意:无论什么编码在内存里显示字符,存在硬盘里都是二进制。
编码的转换:
GBK中文编码←→Unicode←→Shift-JIS
(GBK decode到Unicode,Unicode encode到Shift-JIS decode解码、encode编码)
Python bytes类型
bytes类型,即字节类型, 它把8个二进制一组称为一个byte,用16进制来表示。
Python 2 搞了一个新的字符类型,就叫unicode类型,比如你想让你的中文在全球所有电脑上正常显示,在内存里就得把字符串存成unicode类型。
PY3 除了把字符串的编码改成了unicode, 还把str 和bytes 做了明确区分, str 就是unicode格式的字符, bytes就是单纯二进制啦。
3.4文件处理
python处理文件
例一:
f=open(file'D:/工作日常/兼职白领学生空姐模特护士联系方式.txt',mode='r',encoding='utf-8') data=f.read() f.close() 上边的语法解释: f=open(file'D:/工作日常/兼职白领学生空姐模特护士联系方式.txt' 表示文件路径 mode='r' 表示只读(可以改为其他) encoding=’uft-8' 表示将硬盘上的0101010按照uft的规则“断句”后的每一段0101010转换成unicode的01010101,unicode对照表中有01010101和字符的对应关系。 f.read() 表示读取所有内容,内容是已经转换完毕的字符串 f.close() 表示关闭文件
注意:此处的encoding必须和文件在保存时设置的编码一致,不然“断句”会不准确从而造成乱码。
例二:
f = open(file='D:/工作日常/兼职白领学生空姐模特护士联系方式.txt',mode='rb') data = f.read() f.close() 上边语法解释: file='D:/工作日常/兼职白领学生空姐模特护士联系方式.txt' 表示文件路径 mode='rb' 表示只读(可以修改为其他) f.read() 表示读取所有内容,内容是硬盘上原来以某种编码保存的 010101010,即:某种编码格式的字节类型 f.close() 表示关闭文件
例一与例二的区别:在于示例2打开文件时并未指定encoding,直接以rb模式打开了文件 ,rb是指二进制模式,数据读到内存里直接是bytes格式。
注意:
- 文件操作时,以 “r”或“rb” 模式打开,则只能读,无法写入;
- 硬盘上保存的文件都是某种编码的0101010,打开时需要注意:
- rb,直接读取文件保存时原生的0101010,在Python中用字节类型表示
- r和encoding,读取硬盘的0101010,并按照encoding指定的编码格式进行断句,再将“断句”后的每一段0101010转换成unicode的 010101010101,在Python中用字符串类型表示
循环文件
f=open("兼职白领学生空姐模特护士联系方式。txt",'r',encoding="uft-8") for line in f: print(line) f.close()
写文件
f.=open(file=‘D:/工作日常/兼职白领学生空姐模特护士联系方式.txt,mode='w',encoding='uft-8') f.write('北大本科美国留学一次50,微信号:xxxxx') f.close() 上边语法解释: file='D:/工作日常/兼职白领学生空姐模特护士联系方式.txt 表示文件路径 mode='w' 表示只写 encoding='utf-8' 将要写入的unicode字符串编码成utf-8格式 f.write(...) 表示写入内容,写入的内容是unicode字符串类型,内部会根据encoding转换为制定编码的 01101010101,即:字节类型 f.close()
二进制写
f = open(file='D:/工作日常/兼职白领学生空姐模特护士联系方式.txt',mode='wb') f.write('北大本科美国留学一次50,微信号:xxxxx'.encode('utf-8')) f.close() 上边语法解释: file='D:/工作日常/兼职白领学生空姐模特护士联系方式.txt' 表示文件路径 mode='wb' 表示只以2进制模式写 f.write(...) 表示写入内容,写入的内容必须字节类型,即:是某种编码格式的0101010 f.close()
注意:
文件操作时,以 “w”或“wb” 模式打开,则只能写,并且在打开的同时会先将内容清空。
写入到硬盘上时,必须是某种编码的0101010,打开时需要注意:
- wb,写入时需要直接传入以某种编码的0100101,即:字节类型
- w 和 encoding,写入时需要传入unicode字符串,内部会根据encoding制定的编码将unicode字符串转换为该编码的 010101010
追加:把内容追加到文件底部
f = open("兼职白领学生空姐模特护士联系方式.txt",'a',encoding="gbk") f.write("\n杜姗姗 北京 167 49 13324523342") f.close()
注意:
文件操作时,以 “a”或“ab” 模式打开,则只能追加,即:在原来内容的尾部追加内容
写入到硬盘上时,必须是某种编码的0101010,打开时需要注意:
- ab,写入时需要直接传入以某种编码的0100101,即:字节类型
- a 和 encoding,写入时需要传入unicode字符串,内部会根据encoding制定的编码将unicode字符串转换为该编码的 010101010
读写模式
f = open("兼职白领学生空姐模特护士联系方式.txt",'r+',encoding="gbk") data = f.read() #可以读内容 print(data) f.write("\nblack girl 河北 167 50 13542342233") #可以写 f.close()
不能添加到任意位置,W+会把之前的内容清除。
文件操作的其他功能;
def fileno(self, *args, **kwargs): # real signature unknown 返回文件句柄在内核中的索引值,以后做IO多路复用时可以用到 def flush(self, *args, **kwargs): # real signature unknown 把文件从内存buffer里强制刷新到硬盘 def readable(self, *args, **kwargs): # real signature unknown 判断是否可读 def readline(self, *args, **kwargs): # real signature unknown 只读一行,遇到\r or \n为止 def seek(self, *args, **kwargs): # real signature unknown 把操作文件的光标移到指定位置 *注意seek的长度是按字节算的, 字符编码存每个字符所占的字节长度不一样。 如“路飞学城” 用gbk存是2个字节一个字,用utf-8就是3个字节,因此以gbk打开时,seek(4) 就把光标切换到了“飞”和“学”两个字中间。 但如果是utf8,seek(4)会导致,拿到了飞这个字的一部分字节,打印的话会报错,因为处理剩下的文本时发现用utf8处理不了了,因为编码对不上了。少了一个字节 def seekable(self, *args, **kwargs): # real signature unknown 判断文件是否可进行seek操作 def tell(self, *args, **kwargs): # real signature unknown 返回当前文件操作光标位置 def truncate(self, *args, **kwargs): # real signature unknown 按指定长度截断文件 *指定长度的话,就从文件开头开始截断指定长度,不指定长度的话,就从当前位置到文件尾部的内容全去掉。 def writable(self, *args, **kwargs): # real signature unknown 判断文件是否可写
修改文件
f1 = open("luffy.txt",'w',encoding="utf-8") f1.write("[路飞学城]") f1.close() f = open("luffy.txt",'r+',encoding="utf-8") f.write("alex") f.close()
占硬盘方式的文件修改代码示例
f_name = "兼职白领学生空姐模特护士联系方式utf8.txt" f_new_name = "%s.new" % f_name old_str = "乔亦菲" new_str = "[乔亦菲 Yifei Qiao]" f = open(f_name,'r',encoding="utf-8") f_new = open(f_new_name,'w',encoding="utf-8") for line in f: if old_str in line: new_line = line.replace(old_str,new_str) else: new_line = line f_new.write(new_line) f.close() f_new.close()
上面的代码,会生成一个修改后的新文件 ,原文件不动,若想覆盖原文件
import os f_name = "兼职白领学生空姐模特护士联系方式utf8.txt" f_new_name = "%s.new" % f_name old_str = "乔亦菲" new_str = "[乔亦菲 Yifei Qiao]" f = open(f_name,'r',encoding="utf-8") f_new = open(f_new_name,'w',encoding="utf-8") for line in f: if old_str in line: new_line = line.replace(old_str,new_str) else: new_line = line f_new.write(new_line) f.close() f_new.close() os.rename(f_new_name,f_name) #把新文件名字改成原文件 的名字,就把之前的覆盖掉了,windows使用os.replace # 帮助文档说明replace会覆盖原文件
3.4函数
函数在BASIC中叫做subroutine(子过程或子程序),在Pascal中叫做procedure(过程)和function,在C语言当中只有function,在JAVA中叫做method。
定义:函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可
特性:
1.减少重复代码
2.使程序变得可扩展
3.使程序变得易维护
语法定义
def sayhi():#函数名 print(“Hello,I‘m nobody!”) sayhi()#调用函数
有参数的情况下
#下边这段代码a,b=5,8 c=a**b print(c) #改成用函数写 def calc(x,y): res=x**y return res #返回执行函数结果 c=calc(a,b) #结果赋值给C变量 print(C)
函数参数
形参变量:只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量。
实参:可以是常量、表达式、函数等,无论是何种类型的量,在进行函数调用时,他们都必须有确定的值,以便把这些值传给形参。因此应预先用赋值,输入等办法使参数获得确定值。
def calc(x,y): #x y 为形参 res=x**y return res c=calc(a,b) #a b为实参 print(c)
默认参数
把一个名词变成默认参数:
def stu_register(name,age,course,country="CN")
这样这个参数在调用时不指定,那就是默认的CN,指定的话,就是你指定的值。
注:把CN变成默认参数后把他的位置放在最后。
关键参数正常情况下,给函数传函数要按顺序,不想按顺序就可以用关键参数,只需指定参数名即可。指定参数名的参数就叫关键参数),但记住有一个要求就是,关键参数必须放在位置参数(以
位置顺序确定对应关系的参数)之后。
例如,调用时这样用
stu_register("王山炮“,course='PY',age=22,country='JP')
不可以这样用
stu_register("王山炮“,course='PY',22,country='JP')
更不能这样
stu_register("王山炮“,22,age=25,country='JP')
这样相当于两次给age赋值,会会报错。
非固定参数
若你的函数定义时不确定用户想传入多少个参数,就可以使用非固定函数
def stu_register(name,age,*args):#*args会把多传入的参数变成一个元素形式 print(name,age,args) stu-register("Alex",22) #输出 #Alex 22()#后边这个()就是args,只是因为没传值,所以为空 stu-register(“Jack”,32,“CN”,“Python") #输出 #jack 32 ('CN','Python')
还有一种**kwargs
def stu_register(name,age,*args,**kwargs): # *kwargs 会把多传入的参数变成一个dict形式 print(name,age,args,kwargs) 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'}
返回值
函数外边的代码要想获取函数的执行结果,就可以在函数里用return语句把结果返回。
def stu_register(name, age, course='PY' ,country='CN'): print("----注册学生信息------") print("姓名:", name) print("age:", age) print("国籍:", country) print("课程:", course) if age > 22: return False else: return True registriation_status = stu_register("王山炮",22,course="PY全栈开发",country='JP') if registriation_status: print("注册成功") else: print("too old to be a student.")
注意:
1.函数执行过程中只要遇到return语句,就会停止执行并返回结果,那么也可以理解为return语句代表函数的结束。
2.如果未在函数中指定return,那么函数的返回值为None
全局与局部变量
name="Alex Li“ def change_name(name): print("before change:",name) name="金角大王,一个有的tesla的男人” print("after change",name) change_name(name) print("在外边看看name改了吗?“,naem)
输出
before change:ALex Li
after change 金角大王,一个有tesla的男人
在外边看看name改了吗?Alex Li
name = "Alex Li" def change_name(): name = "金角大王,一个有Tesla的男人" print("after change", name) change_name() print("在外面看看name改了么?", name)
但是不能这样改:
- 在函数中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。
- 全局变量作用域是整个程序,局部变量作用域是定义该变量的函数。
- 当全局变量与局部变量同名时,在定义局部变量的函数内,局部变量起作用;在其它地方全局变量起作用。
作用域
作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
在函数里修改全局变量:
name = "Alex Li" def change_name(): global name name = "Alex 又名 金角大王,路飞学城讲师" print("after change", name) change_name() print("在外面看看name改了么?", name)
global name的作用就是要在函数里声明全局变量name ,意味着最上面的name = "Alex Li"
即使不写,程序最后面的print也可以打印name
嵌套函数
name = "Alex" def change_name(): name = "Alex2" def change_name2(): name = "Alex3" print("第3层打印", name) change_name2() # 调用内层函数 print("第2层打印", name) change_name() print("最外层打印", name)
输出
第3层打印 Alex3
第2层打印 Alex2
最外层打印 Alex
匿名函数
匿名函数就是不需要显示的制定函数名
#这段代码 def calc(x,y): return x**y print(calc(2,5)) #换成匿名函数 calc = lambda x,y:x**y print(calc(2,5))
高阶函数
变量可以指向函数,函数的参数可以接受变量,那么一个函数就可以接受另一个函数作为参数,这种函数称之为高阶函数。
def add(x,y,f): return f(x) + f(y) res = add(3,-6,abs) print(res)
只需满足一下任意一个条件就是高阶函数
接受一个或多个函数作为输入
return返回另外一个函数
递归
在函数内部,可以调用其他函数。如果一个函数的内部调用自身本身,这个函数就是递归函数。
def calc(n): print(n) if int(n/2) ==0: return n return calc(int(n/2)) calc(10)
输出
10 5 2 1
递归特性:
- 必须有一个明确的结束条件
- 每次进入更深一层递归时,问题规模相比上次递归都应有所减少
- 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
内置函数
几个古怪内置方法的提醒
#compile f = open("函数递归.py") data =compile(f.read(),'','exec') exec(data) #print msg = "又回到最初的起点" f = open("tofile","w") print(msg,"记忆中你青涩的脸",sep="|",end="",file=f) # #slice # a = range(20) # pattern = slice(3,8,2) # for i in a[pattern]: #等于a[3:8:2] # print(i) # # #memoryview #usage: #>>> memoryview(b'abcd') #<memory at 0x104069648> #在进行切片并赋值数据时,不需要重新copy原列表数据,可以直接映射原数据内存, import time for n in (100000, 200000, 300000, 400000): data = b'x'*n start = time.time() b = data while b: b = b[1:] print('bytes', n, time.time()-start) for n in (100000, 200000, 300000, 400000): data = b'x'*n start = time.time() b = memoryview(data) while b: b = b[1:] print('memoryview', n, time.time()-start)