python自动化编程-第三天
python自动化编程-第三天 函数 集合 文件 编码
一、集合操作
集合是一个无序的,不重复的数据组合,它的主要作用如下:
- 去重,把一个列表变成集合,就自动去重了
- 关系测试,测试两组数据之前的交集、差集、并集等关系
1.创建集合
>>> list_1 = [1,2,3,4,5,6,3,3,1,2,3,4,4]
>>> set_1 = set(list_1)
>>> print(set_1,type(set_1))
{1, 2, 3, 4, 5, 6} <class 'set'>
>>>
2.求交集
>>> list_1 = [1,2,3,4,5,6,3,3,1,2,3,4,4]
>>> set_1 = set(list_1)
>>> print(set_1,type(set_1))
{1, 2, 3, 4, 5, 6} <class 'set'>
>>>
>>>
>>> list_2 = [3,4,5,6]
>>>
>>> set_2 = set(list_2)
>>> print(set_2,type(set_2))
{3, 4, 5, 6} <class 'set'>
>>>
>>> print(set_1.intersection(set_2)) #求交集
{3, 4, 5, 6}
>>> print(set_1 & set_2) #求交集
{3, 4, 5, 6}
3.求并集
>>> print(set_1.union(set_2))
{1, 2, 3, 4, 5, 6}
>>>
>>>
>>> print(set_1 | set_2)
{1, 2, 3, 4, 5, 6}
4.差集
>>> print(set_1.difference(set_2)) #in set_1 but not in set_2
{1, 2}
>>> print(set_1 - set_2)
{1, 2}
5.子集
>>> print(set_2.issubset(set_1)) #set_2 是set_1的子集
True
>>> print(set_1.issuperset(set_2)) #set_1 是set_2的父集
True
6.对称差集
>>> print(set_1.symmetric_difference(set_2)) #将两个集合合并,然后删除交集的集合
{1, 2}
7.无交集
>>> list_3 = [33,44,55,66]
>>> set_3 = set(list_3)
>>> print(set_3)
{33, 66, 44, 55}
>>>
>>> print(set_1.isdisjoint(set_3)) #无交集为True
True
8.增加
>>> print(set_1)
{1, 2, 3, 4, 5, 6}
>>> set_1.add(7) #一次只能增加一个元素
>>> print(set_1)
{1, 2, 3, 4, 5, 6, 7}
>>>
>>> set_1.update([33,44,55]) #一次可以增加多个元素,但是多个元素要以列表的形式;
>>> print(set_1)
{1, 2, 3, 4, 5, 6, 7, 33, 44, 55}
>>>
9.删除
>>> set_1.remove(55) #删除指定的元素
>>> print(set_1)
{1, 2, 3, 4, 5, 6, 7, 33, 44}
>>> set_1.pop() #随机删除
1
>>> set_1.pop()
2
>>> set_1.pop()
3
>>> print(set_1)
{4, 5, 6, 7, 33, 44}
>>>
>>> set_1.discard(33) #删除指定元素
>>> print(set_1)
{4, 5, 6, 7, 44}
>>> set_1.discard(77) #remove与discard的区别,如果删除的是一个不存在的元素,remove会报错,而discard不会;
>>> set_1.remove(77)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 77
10.成员判断
>>> 5 in set_1 #在python中所有数据类型的成员判断都收这么写的;
True
二、文件操作
对文件操作流程
- 打开文件,得到文件句柄并赋值给一个变量
- 通过句柄对文件进行操作
- 关闭文件
如果直接对打开的文件进行操作,会出现的问题:
- 打开的文件在内存中始终占用一块内存空间;
- 无法关闭打开的文件句柄
- 无法进行后续相关操作
1.打开文件
>>> f = open("yesterday","r",encoding="utf-8") #文件句柄,就是文件的内存对象,包含文件名、文件大小,字符集,在硬盘上的起始位置;
>>> data = f.read() #文件读取时,是有一个指针的,当第一次读完以后,指针就会在文件的尾端,
>>> print(data)
Somehow, it seems the love I knew was always the most destructive kind
不知为何,我经历的爱情总是最具毁灭性的的那种
Yesterday when I was young
昨日当我年少轻狂
The taste of life was sweet
生命的滋味是甜的
>>>
打开文件的模式有:
r,只读模式(默认)。
w,只写模式。【不可读;不存在则创建;存在则删除内容;】
a,追加模式。【不可读;不存在则创建;存在则只追加内容;】
"+" 表示可以同时读写某个文件
r+,可读写文件。【可读;可写;可追加】
w+,写读
a+,可最加,可读
"U"表示在读取时,可以将 \r \n \r\n自动转换成 \n (与 r 或 r+ 模式同使用)
rU
r+U
"b"表示处理二进制文件(如:FTP发送上传ISO镜像文件,linux可忽略,windows处理二进制文件时需标注),二进制文件是不能有字符集参数的
rb
wb
ab
2.文件的其他方法
>>> f.read() #此时在进行读操作,将没有任何内容;
''
>>> f.seek(0) #将文件的指针移动到指定的位置,表示文件的开头,whence:可选,默认值为 0。给offset参数一个定义,表示要从哪个位置开始偏移;0代表从文件开头开始算起,1代表从当前位置开始算起,2代表从文件末尾算起。
0
>>> f.tell() #显示当前的指针位置;
>>> f.read() #读取文件内容,带有‘\n’等符号
"Somehow, it seems the love I knew was always the most destructive kind\n不知为何,我经历的爱情总是最具毁灭性的的那种\nYesterday when I was young\n昨日当我年少轻狂\nThe taste of life was sweet\n生命的滋味是甜的"
>>>
>>> f.readline() #根据seek的值,开始读取一行;
>>>
>>> f.readlines() # 读取文件的所有行,形成一个列表的元素,带有‘\n’等符号,
>>>
>>>f.write() #追加内容至文件中
>>>f.detach() #在文件编辑中,将文件的编码改成utf-8,一般不用
>>> f.encoding #打印文件的编码格式
'utf-8'
>>> f.fileno() #返回一个文件句柄编号;操作系统接口的一个编号;
3
>>> f.isatty() #与打印机交互时,可能要用到
False
>>> f.seekable() #不是所有的文件都可以移动文件指针,像设备文件就不可以移动文件指针,此参数就是判断文件是否可以移动指针的,True表示可以移动。
True
>>> f.readable() #判断文件是否可读
True
>>> f.writable() #判断文件是否可写
False
>>> f.flush() #刷新,默认情况下文件打开时,设置的有缓存,会将所有的写操作放置在缓存中,当缓存满了才会写到硬盘中;强制刷新,将缓存的数据直接写到硬盘中,而不是等待缓存满了
>>> f.buffer() #查看文件缓存在内存中的对象
>>> f.closed #判断文件是否关闭
>>> f.truncate(10) #截断,不写就是清空文件,10表示从开头保留10个字符,不能与seek配合使用;需要些权限才可以使用;
3.动态进度条
使用f.flush()方法来完成;
#打印进度条,因为print默认会自动换行,所以在pycharm中运行;
import sys,time
for i in range(20):
sys.stdout.write("#")
sys.stdout.flush()
time.sleep(0.1)
####################
4.文件修改
文件修改的两种方式:
1、加载到内存中去修改,不建议使用,可以修改小文件。但是这样修改只会覆盖原来的数据;
2、打开一个新文件,将源文件的数据拷贝到新文件中,再修改,完成以后覆盖源文件
在读取文件时,如果只是一行一行的处理,则直接 for line in file即可,如果file.readlines()
下面这个例子类似于linux中的sed命令;
import sys
f = open("yesterday2","r",encoding="utf-8")
f_new = open("yesterday2.bak","w",encoding="utf-8")
find_str = sys.argv[1]
replace_str = sys.argv[2]
for line in f:
if find_str in line:
line = line.replace(find_str,replace_str)
f_new.write(line)
f.close()
f_new.close()
5.文件关闭
为了避免打开文件后忘记关闭,可以使用with语句来自动关闭文件;
with open("yesterdat","r",encoding="utf-8") as f:#自动关闭文件,也可以同时打开多个文件,
for line in f:
print(line)
with open("yesterdat2","r",encoding="utf-8") as f ,\
open("aaa","r",encoding="utf-8"):
根据PE8规范,一行代码不能超过80个字符,因此需要使用反斜杠,换行;
三、字符编码与转码
1.字符编码介绍
Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,规定虽有的字符和符号最少由 16 位来表示(2个字节),即:2 **16 = 65536。
注:此处说的的是最少2个字节,可能更多UTF-8,是对Unicode编码的压缩和优化,扩展集,他不再使用最少使用2个字节,而是将所有的字符和符号进行分类:ascii码中的内容用1个字节保存、欧洲的字符用2个字节保存,东亚的字符用3个字节保存...
从ASCII、GB2312、GBK 到GB18030,这些编码方法是向下兼容的;
有的中文Windows的缺省内码还是GBK,可以通过GB18030升级包升级到GB18030。不过GB18030相对GBK增加的字符,普通人是很难用到的,通常我们还是用GBK指代中文Windows内码。
打印python默认编码
import sys
print(sys.getdefaultencoding())
2.编码声明的作用
python3 读取代码的默认编码是utf-8,
python2读取代码的默认编码是ascii;
通常在保存代码文件时,首先要确定代码文件是由什么格式的编码保存在物理磁盘上的;然后在代码文件的头部声明改编码格式;这样python才会以声明的编码格式来读物代码文件;
3.编码的转换
所有的字符编码转换都要先转换成unicode,然后再通过unicode来转换成想要的编码格式,因此任何一种字符编码的decode,都是讲字符解码成unicode编码。

例如unicode、GBK、UTF-8,ASCII的关系;
- utf-8是unicode的扩展集,所以utf-8的编码格式可以直接打印unicode编码格式的字符;
- unicode是支持GBK的,但是要向答应出GBK编码的字符,首先需要进行转换编码格式,以后才能打印。
- UTF-8和unicode都包含了ASCii的编码格式;
- python3里的encode不仅转换了编码,并且转换成了byte类型
另外python2中字符串表示字符串和byte类型,而在python3中字符串表示unicode类型和字符串类型。
在python2中,字符串前面加u 表示是一个unicode的编码,而python3中,字符串前面加b,表示一个byte类型的编码。
4.问题
1、cmd下的乱码问题
hello.py
1 #coding:utf8
2 print ('苑昊')
文件保存时的编码也为utf8。
思考:为什么在IDE下用2或3执行都没问题,在cmd.exe下3正确,2乱码呢?
我们在win下的终端即cmd.exe去执行,大家注意,cmd.exe本身也一个软件;当我们python2 hello.py时,python2解释器(默认ASCII编码)去按声明的utf8编码文件,而文件又是utf8保存的,所以没问题;问题出在当我们print'苑昊'时,解释器这边正常执行,也不会报错,只是print的内容会传递给cmd.exe用来显示,而在py2里这个内容就是utf8编码的字节数据,可这个软件默认的编码解码方式是GBK,所以cmd.exe用GBK的解码方式去解码utf8自然会乱码。
py3正确的原因是传递给cmd的是unicode数据,cmd.exe可以识别内容,所以显示没问题。
明白原理了,修改就有很多方式,比如:
1 print (u'苑昊')
改成这样后,cmd下用2也不会有问题了。
2、open()中的编码问题
创建一个hello文本,保存成utf8:
苑昊,你最帅!
同目录下创建一个index.py
f=open('hello')
print(f.read())
为什么 在linux下,结果正常:苑昊,在win下,乱码:鑻戞槉(py3解释器)?
因为你的win的操作系统安装时是默认的gbk编码,而linux操作系统默认的是utf8编码;
当执行open函数时,调用的是操作系统打开文件,操作系统用默认的gbk编码去解码utf8的文件,自然乱码。
解决办法:
f=open('hello',encoding='utf8')
print(f.read())
如果你的文件保存的是gbk编码,在win 下就不用指定encoding了。
另外,如果你的win上不需要指定给操作系统encoding='utf8',那就是你安装时就是默认的utf8编码或者已经通过命令修改成了utf8编码。
注意:open这个函数在py2里和py3中是不同的,py3中有了一个encoding=None参数。
四、函数
编程语言中函数的定义:函数是逻辑结构化和过程化的一种编程方法;简单来说就是:函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数,只需调用其函数名即可。
函数式编程的重要特点就是,只要输入是确定的,输出就是确定的,因为里面不包含任何的逻辑运算;
特性:
减少重复代码
使程序变的可扩展
使程序变得易维护
#函数
def func1():
"""testing"""
print("in the func1")
return 0
#过程
def func2():
print("in the func2")
x = func1()
y = func2()
print("form func1 is %s" %x)
print("form func2 is %s" %y)
1.形参与实参
形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参变量,简单来说形参就是定义函数时使用的参数;
实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值;简单来说实参就是调用函数时使用的参数;

2.位置参数和关键字参数
正常情况下,给函数传参数要按形参的顺序,不想按顺序就用关键字参数,只需指定参数名即可,但记住一个要求就是,关键参数必须放在位置参数之后。并且关键字参数的数量要与位置参数的数量相同;如果有默认参数的话,关键字参数可以少于位置参数;
def test(x,y):
print(x)
print(y)
test(1,2) #与形参一一对应
test(y=2,x=1) #与形参顺序无关
#test(x=2,3) #报错
test(3,y=2) #可以调用
#test(3,x=2) #报错
3.默认参数
默认参数就是在定义函数时,给形参赋一个值,该值就是这个形参的默认参数;
def test(x,y=2):
print(x)
print(y)
test(1,4)
默认参数的特点:调用函数的时候,默认参数非必须传递,
4.非固定参数(参数组)
def test(*args): #args表示一个变量名,一般就用args
print(args)
test(1,2,3,4,5)
test(*[1,2,3,4,5]) # *args = *[1,2,3,4,5] ---> args =tuple([1,2,3,4,5])
>>> def test1(x,*args):
... print(x)
... print(args)
...
>>> def test(*args):
... print(args)
...
>>> test(*[1,2,3,4,5])
(1, 2, 3, 4, 5)
>>> test(1,2,3,4,5)
(1, 2, 3, 4, 5)
>>> test1(*[1,2,3,4,5]) # x =1,*args = *[2,3,4,5] ---> args =tuple([2,3,4,5])
1
(2, 3, 4, 5)
>>> test1(1,2,3,4,5,6)
1
(2, 3, 4, 5, 6)
>>>
def test2(**kwargs):
print(kwargs)
print(kwargs['name'])
print(kwargs['age'])
print(kwargs['sex'])
test2(name="alex",age=8,sex="F")
test2(**{"name":'alex',"age":8,"sex":'F'})
def test3(name,**kwargs):
print(name)
print(kwargs)
test3('alex',age=18,sex='M')
def test4(name,age=18,**kwargs):
print(name)
print(age)
print(kwargs)
test4('alex',sex='m',hobby='tesla',age=3)
*args:接收n个位置参数,转换成元组的形式
**kwargs:把N个关键字参数,转换成字典的方式
参数组一定要放在所有形参的后面;一般情况下,参数组用在实参不固定的时候;
5.返回值
函数的返回值:想要函数执行的结果,才是返回值的含义;也就是说后面的代码需要函数的执行的结果进行数据处理;
def test1():
print("in the test1")
return 0
print("end")
x = test1()
print(x)
特点:
return 后面的语句不会执行
return 返回值可以赋值给一个变量,
def test1():
print("in the test1")
def test2():
print("in the test2")
return 0
def test3():
print("in the test3")
return 1,"hello",["alex","wupeiqi"],{"name":"alex"}
x = test1()
y = test2()
z = test3()
print(x,y,z)
返回值:
返回值数=0:返回None
返回值数=1:返回object
返回值数>1:返回tuple
6.局部变量
变量都是作用域,局部变量作用域一般只在函数内部;
全局变量:在整个程序都生效的变量,在代码的顶级定义的变量,后面的子级都可以调用;
局部变量变成全局变量,需要在函数内部要声明一下:global VAR
school = "oldboy edu."
name = 'alex'
def change_name(name):
global school #声明成全局变量,不要这么使用
school = "Mage linux"
print("before change",name,school)
name="Alex Li" #这个函数就是这个变量的作用域
age =23
print("after change",name)
print("before def,$school:",school)
change_name(name)
print("after def,$name:",name)
print("after def,$school",school)
-----------
before def,$school: oldboy edu.
before change alex Mage linux
after change Alex Li
after def,$name: alex
after def,$school Mage linux
一般情况下,不应该在函数里面声明全局变量,
另外函数还有一个特性:列表、字典、集合、类,都可以在函数内部改,字符串和整数在函数内部是不能改的;
7.递归函数
如果一个函数在内部调用自身,就叫递归函数。
特性:
1、必须要有一个明确的结束条件,最大的递归次数是999层,死循环的递归会报错,
2、每次进入更深一层递归时,问题规模相比上次递归都应有所减少
3、递归效率不高,递归层次多会导致栈溢出,在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)
def calc1(n):
print(n)
if int(n/2)>0:
return calc1(int(n/2))
print("--->",n)
calc1(5)
-----------
calc1(5)
calc1(2)
calc1(1)
calc1(0)
8.高阶函数
变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
def add(a,b,f): #abs绝对值的函数
return f(a)+f(b)
res = add(2,-6,abs)
print(res)
9.函数的调用
程序的执行是从上到下读取代码文件执行的,因此被父函数调用的函数,要在父函数被调用之前定义好;
10.字符串转换成字典的方法
>>> b = ''' {
... 'name' : 'aaa',
... 'age': 33
... }'''
>>>
>>> a = eval(b)
>>> print(a)
{'name': 'aaa', 'age': 33}
>>> print(type(a))
<class 'dict'>


浙公网安备 33010602011771号