文件操作:
打开文件的方法:
第一种:打开文件执行完命令后,需要关闭文件,否则会一直占用内存空间
f=open('/data/1.txt','r+') #后面的r+ 表示打开文件的模式是读写
#命令
f.close #关闭文件
第二种:这种会自动关闭文件,不用我们手动关闭,这种写法被称为python中的语法糖。
with open('/data/1.txt','r+') as f:
#命令
##################
打开文件的模式:
r : 只读模式,默认如果不加模式就是只读模式
r+: 读写模式,读,写,追加,如果文件不存在会报错。
w:只写模式 ,会覆盖文件内容,如果文件不存在会创建
w+: 写读模式 ,会覆盖文件内容,如果文件不存在会创建
a:追加模式 ,如果文件不存在会创建
a+:和a的意思一样
b :二进制(字节)模式处理文件。 当需要处理二进制文件时,需要加一个b,比如rb,wb,ab, 需要注意的是b的方式在打开或写入时不能指定编码格式,只能打开后加.decode('utf-8')进行解码, 用wb方式写入时需要将字符串转化为字节
###########################################################
小例子:编码错误
#!/usr/bin/env python
# encoding:utf-8
f = open('歌词','r')
data = f.read()
f.close()
print(data)
#执行结果:
UnicodeDecodeError: 'gbk' codec can't decode byte 0x80 in position 22: illegal multibyte sequence
#分析
当时编辑 '歌词' 这个文件时,我的编辑器的编码是utf-8 , 所以 '歌词' 这个文件编码就是utf-8, 现在用open()函数打开,open()函数会去找系统的默认编码,windows的系统一般是gbk编码格式的,所以会报错,而mac默认编码就是utf-8,打开就不会报错了,这里的解决办法是在打开文件时,指定编码格式
# 指定编码格式
f = open('歌词','r',encoding='utf-8')
data = f.read()
f.close()
print(data)
小例子: 二进制模式读取文件
#错误示例:
f = open('666.txt','rb',encoding='utf-8')
data = f.read()
f.close()
print(data)
#执行结果
ValueError: binary mode doesn't take an encoding argument
# 二进制模式处理文件时,不能用encoding 指定编码
#正确示例:
f = open('666.txt','rb')
data = f.read()
f.close()
print(data)
#执行结果:
b'66666\r\n2312313'
# 注意windows 中的换行符是 \r\n 而linux中是\n 开头的b表示字节
#特别注意:当666.txt 这个文件里有中文,这个时候print (data) 会出现 xe4\xb8 这种16进制的,无法看到中文,这个时候需要用.decode()解码,print(data.decode())
小例子:二进制模式写文件
#错误示例
txt1='''
又一年花如绣,山中雪埋红豆。
曾是谁把盏问,可饮一杯否。
'''
f=open(txt1.log,'wb')
f.write(txt1)
f.close()
#执行结果
TypeError: a bytes-like object is required, not 'str'
#字符串不能用b写入,必须转化成字节
#正确示例
f=open('txt1.log','wb')
f.write(txt1.encode(encoding='utf-8'))
f.close()
#分析:因为用b写文件必须转成字节格式,所以用encode()编码,encode()里面可以指定编码格式,我这里是utf8,特别注意,前面说过b模式不能指定编码格式,说的是用open()函数打开的时候不能指定,我在打开后指定的
#bytes() 函数也可以转换成字节,bytes(txt1,encoding='utf-8')
#字符串 ---------- encode() -----------> 字节 ,编码过程
#字节 ---------- decode() -----------> 字符串 ,解码过程
下面来看用with处理文件的小例子:
txt1='''
又一年花如绣,山中雪埋红豆。
曾是谁把盏问,可饮一杯否。
'''
with open('123.txt','a') as f:
f.write(txt1)
with open('123.txt','r') as f:
a=f.read()
with open('123.txt','r') as f:
b=f.read(9)
print(a)
print(b)
#执行结果:
又一年花如绣,山中雪埋红豆。
曾是谁把盏问,可饮一杯否。
又一年花如绣,山
上面这段代码会把txt1中的内容追加到123.txt这个文件中。 然后用read()读出全部赋值给a并打印出来,也可以指定读取的字符长度,赋值给b打印出来。但是:如果文件大的话不要用read()这种方法,可能不能全部读出来
特别注意:文件操作中除了read()方法 是读取的字符,其他方法都是读取的字节,在utf-8编码中,一个汉字占3个字节
除了用read读之外还好有其他方式,下面来介绍:
with open('123.txt','r') as f:
line1=f.readline()
print (line1)
line1的结果: 又一年花如绣,山中雪埋红豆。
说明readline()只读了一行。 如果要读全部内容那就用循环去一行一行的读呗,如下方法:
with open('123.txt','r') as f:
while True:
line1=f.readline()
if len(line1) == 0:
break
print (line1)
readline()用这种循环的方法就把全部内容都读了出来!
with open('123.txt','r') as f:
line2=f.readlines()
print (line2)
line2的结果:['又一年花如绣,山中雪埋红豆。\n', '曾是谁把盏问,可饮一杯否。\n']
说明readlines() 全读出来了,但读出来的是一个列表。 当文件很大的时候不建议用这种 ,可能会出现卡住的情况
小例子:write写文件的时候只能写字符串 , writelines 可以写列表,但是列表中的元素必须是字符串
f = open('1.txt','w',encoding='utf-8')
#用write写的时候只能写字符串
f.write('11111111111\n')
#如果不加换行符,会写在一行里
f.write('2222222222222\n')
#用writelines 可以写列表,但是列表中的元素必须是字符串
f.writelines(['1\n','2\n'])
f.close()
获取真正的换行符
# 前面的例子说过,二进制模式rb读取文件时,可以看到windows系统的换行符是 \r\n
# 假如不用二进制模式打开,用默认的文本模式打开看到换行符不是 \r\n
f = open('1.txt','r')
data = f.readlines()
f.close()
print(data)
#执行结果:
['11111111111\n', '2222222222222\n', '1\n', '2\n']
# 分析,因为python 会处理来自不同系统的文件,所以python内部做了转换,统一把换行符转成 \n ,如果要显示真实的换行符 可以在打开文件时加 newline=''
f = open('1.txt','r',newline='')
data = f.readlines()
f.close()
print(data)
# 执行结果:
['11111111111\r\n', '2222222222222\r\n', '1\r\n', '2\r\n']
文件游标
with open('333.txt','w') as f:
f.write(txt3)
with open('333.txt','r') as f:
print(f.tell())
print (f.readline().strip()) #strip表示去掉换行符和空格
print(f.readline().strip())
print(f.tell())
f.seek(0)
print(f.readline().strip())
#执行结果:
0
121212121233
qwertyu
23
121212121233
说明:tell() 方法会显示当前的指针(游标)位置,第一次肯定是0,然后读了两行,那指针位置就是在两行之后,然后用seek(0)方法把指针位置调整到0,seek()方法控制指针的位置。再次读取一行,还是读取的第一行,因为指针在哪,就从哪里开始读。
seek()方法的高级用法
seek() 方法的作用就是控制指针的位置
seek() 方法其实有三种模式,分别是 0 , 1 , 2 我下面会举例说明
第一种 0模式,从头开始(默认的模式)
text = '''123333456789
qwertyuio
我是好人
666'''
f = open('seek.log','w',encoding='utf-8')
f.write(text)
f.close()
f = open('seek.log','r',encoding='utf-8')
print(f.tell())
f.seek(5)
print(f.tell())
f.seek(6,0)
print(f.tell())
print(f.read())
f.close()
#执行结果:
0
5
6
456789
qwertyuio
我是好人
666
#分析: 第一个f.tell() 肯定是0, 因为文件开头指针肯定是0, 然后f.seek(5)把指针从开头往后移动了5个字节, 那现在的f.tell()就是5, 然后又f.seek(6,0)从头开始移动6个字节, 现在的f.tell() 指针位置就是6, 最后f.read()的结果就是把指针后面的全部读取, 指针在6, 所以把第6个字节后面的全部读取
#这种模式就是0模式, 可以写成 f.seek(5,0), 因为0可以省略, 一般就写 f.seek(5) 就行了
第二种, 1模式,相对路径
f = open('seek.log','rb')
print(f.tell())
f.seek(5)
print(f.tell())
f.seek(6,1)
print(f.tell())
print(f.read().decode())
f.close()
#执行结果:
0
5
11
9
qwertyuio
我是好人
666
#分析: 第一个f.tell() 肯定是0, 因为文件开头指针肯定是0, 然后f.seek(5)这是第一个seek(), 把指针从开头往后移动了5个字节, 那现在的f.tell()就是5, 然后又f.seek(6,1)从当前指针的位置, 也就是第5个字节的位置又移动6个字节, 现在的f.tell() 指针位置就是11, 最后f.read()的结果就是把指针后面的全部读取, 指针在11, 所以把第11个字节后面的全部读取
#特别注意,因为seek()操作的是字节,字节既是二进制模式,除了默认的0模式可以不用b打开,其他两种模式都得用b打开
第三种, 2模式, 倒序
f = open('seek.log','rb')
f.seek(-10,2)
print(f.tell())
print(f.read().decode())
f.close()
# 执行结果:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xa5 in position 0: invalid start byte
#分析: 因为倒数第10个字节在 好 字上,一个汉字占3个字节,好 恰好占了倒数 9,倒数10, 倒数11这三个字节, 不能把汉字拆开,所以报错
f = open('seek.log','rb')
f.seek(-11,2)
print(f.tell())
print(f.read().decode())
f.close()
#执行结果:
31
好人
666
#分析:倒数第11个字节, 也就是从头开始的指针是31, 把倒数第11个字节后的或者说是正序第31个字节后的全部读取, 需要注意换行符是 \r\n 占了两个字节
#特别注意,因为seek()操作的是字节,字节既是二进制模式,除了默认的0模式可以不用b打开,其他两种模式都得用b打开
文件截取 truncate()
text = '''
我们是共产主义接班人
'''
with open('test.log','w') as f:
f.write(text)
with open('test.log','r+') as f:
f.truncate(6)
#查看test.log,发现文件内容是 我们
#分析,在utf-8的编码中一个汉字占3个字节,除了read()是字符,其他方法都是字节。
# 注意:文件截取 truncate(6) 必须是可写的模式打开,但不能是w,因为w直接重新创建文件了,可以是 r+ 或者 a+
再来看文件操作的最后一个例子:
txt2='''请欣赏一首现代诗
黑夜给了我黑色的眼睛
我却用它寻找光明
'''
with open('new.txt','w') as f:
f.write(txt2)
with open('new.txt','r') as f:
first_line=f.readline() #读取第一行内容,指针会移动到第一行的后面,再读的时候就会去第一行后面开始读
print('first_line:',first_line)
print('分割线'.center(50,'-')) #表示以'分割线'为中心,用-补齐50个长度
data=f.read() #读取剩下的内容,
print (data)
# 执行结果:
first_line: 请欣赏一首现代诗
-----------------------分割线------------------------
黑夜给了我黑色的眼睛
我却用它寻找光明
##########################
其他常用文件操作:
f.name #打印文件名字
f.flush() #把文件内容从内存刷到硬盘
f.closed #文件如果关闭则返回True
f.encoding #查看使用open()打开文件的编码, 二进制模式的没有此方法
############################
集合:
集合是一个无序的,不重复的数据类型。
list1=[1,2,3,4,5,6,7,7,7,8,9]
list1=set(list1) #集合会去重
print (list1,type(list1))
结果是:{1, 2, 3, 4, 5, 6, 7, 8, 9} <class 'set'>
结果看上去像排序了的,其实是无序的,数据多了就会看出是无序的。有点像字典,但类型是集合。
集合的方法:
1,增加
list1.add('a') #增加一个元素,可以重复添加相同的值,但是没用,集合去重的
list1.update(['b','c','d']) # 增加多个元素
2, 删除元素
list1.remove('a') #删除元素a,直接写要删除的元素名称就可以 ,但元素不存在时报错
list1.discard('a') #删除元素a,直接写要删除的元素名称就可以 ,元素不存在时不报错
list1.pop() #随机删除一个元素,因为集合是无序的
3,判断成员身份
list1=[1,2,3,4,5,6,7,7,7,8,9]
list1=set(list1)
print (2 in list1) #正确就返回True
print (2 not in list1) #正确就返回True
并集
list1=[1,2,3,4,5,6,7,7,7,8,9]
list1=set(list1)
list2=[1,'a',5,'b',9]
list2=set(list2)
#方法一:
list3=list1|list2
print (list3)
#方法二:
list3=list1.union(list2)
print (list3)
返回结果: {1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b'} 把两个集合的所有元素合并,然后去掉重复的
交集
list1=[1,2,3,4,5,6,7,7,7,8,9]
list1=set(list1)
list2=[1,'a',5,'b',9]
list2=set(list2)
#方法一:
list3=list1 & list2
print (list3)
#方法二:
list3=list1.intersection(list2)
print (list3)
返回结果:{1, 5, 9} 把两个集合的公共的部分取出来
差集
list1=[1,2,3,4,5,6,7,7,7,8,9]
list1=set(list1)
list2=[1,'a',5,'b',9]
list2=set(list2)
#方法一:
list3=list1 - list2
print (list3)
#方法二:
list3=list1.difference(list2) #求list1对list2的差集,就是把list1有的,但list2没有的取出来
print (list3)
返回结果:{2, 3, 4, 6, 7, 8} 求list1对list2的差集,就是把list1有的,但list2没有的取出来
子集与父集
list1=[1,2,3,4,5,6,7,7,7,8,9]
list1=set(list1)
list2=[1,5,9]
list2=set(list2)
print (list2.issubset(list1)) #判断list2是否是list1的子集,也就是说list2的元素是否list1都有,如果是就返回True
print (list1.issuperset(list2)) #判断list1是否是list2的父集,也就是说list2的元素是否list1都有,如果是就返回True
###############################
函数
函数的作用:
1,减少代码重复性
2,使程序变得可扩展
3,使程序变得易维护
定义函数
1,
def func1(): #用def这个关键字去定义函数,func1 是函数名
print ("hello,world") #函数里面的内容
func1() #用函数名加括号的方式去调用这个函数
2,
def func1():
print ("this is func1")
return 0 #返回一个0,注意return代表函数结束,即使后面还有命令也不会执行。
def func2():
print ("this is func2")
return 1,"哈哈",[1,2,3],{1:'a'} #看上去返回一堆东西,把return赋值给一个变量后,实际上打印出来的是一个元组,用元组包了起来
def func3(a,b): #这个函数有参数,叫做形参
res=a**b
return res #返回res的值
x=func1() #执行func1函数并把return的返回值赋值给x,如果没有return,返回的是None
y=func2() #执行func2函数并把return的返回值赋值给y,如果没有return,返回的是None
z=func3(2,3) #执行func3(a,b)函数,并把2传递给a,把3传递给b,执行完命令后把结果res赋值给z,刚才说的a和b叫形参,那么2和3叫实参,就是实际传递的参数
print (x)
print (y)
print (z)
3,默认参数
def stu(name,age,country='china'): #设置country这个参数的值是默认的,有默认参数必须放在最后
print ("your name is %s" %name)
print ("your age is %s" %age)
print ("your country is %s" %country)
stu("zhang",18)
#执行函数时如果不指定第三个参数,那第三个参数就用默认的,如果指定了,那就按照你指定的传参,实参也可以指定固定的,比如stu("zhang",agr=19) 同样,指定的实参也必须放在后面。
#正常情况,给函数传参要按照顺序来的,不按顺序会出错,比如stu("zhang",name="li") 这样会出错,因为zhang已经传递给形参name了,再给name传递一个肯定不行了。
4,非固定参数(如果不确定用户要传入多少参数,就用非固定参数)
def stu(name,age,*args): # args 把用户传入的参数形成一个元组的形式
print ("your name is %s" %name)
print ("your age is %s" %age)
print (args)
stu("zhang",18,'china','you are a good man')
返回结果:
your name is zhang
your age is 18
('china', 'you are a good man')
如果传入的值是类似这种的sex="male",那 *args 就不能处理了,这就用到了 **kwargs
def stu(name,age,**kwargs): #kwargs 会把用户传递的参数形成字典形式
print ("your name is %s" %name)
print ("your age is %s" %age)
print (kwargs)
stu("zhang",18,sex="male",country='china')
返回的结果:
your name is zhang
your age is 18
{'country': 'china', 'sex': 'male'}