3.1、三元运算
概念:三元运算又称三目运算,是对简单的条件语句的简写。
1 if 3>1:
2 val = 1
3 else:
4 val =1o
5
6 改成三元运算后:
7 val = 1 if 3>1 else 10
3.2、文件操作
一、读文件
f = open(file=/Users/crazy_heng/PycharmProjects/untitled/exercise/exercise.txt',mode='r',encoding='utf-8')
其中 f= 后面的是固定语法,file= 是文件的绝对路径➕要读取的文件名
mode= 表示模式,r 代表只读模式
encoding= 表示将硬盘上的文件按后面指定的规则去转码
2 3 data = f.read()
f.read()表示将内容转换成字符串后作为参数传给data这个变量。 4 5 f.close
表示关闭文件夹
二、二进制模式读文件
1 f = open(file=/Users/crazy_heng/PycharmProjects/untitled/exercise/exercise.txt',mode='rb')
2
3 和读文件一样,只不过是模式变成了rb模式
4 data = f. read()
5
6 f.close()
在mode=rb的情况下,是以二进制的模式打开文件,数据读到内容里直接是bytes格式,所以不需要指定encoding,如果想看内容还需要decode一下。
三、导入chardet模块判断文件编码
1 import chardet
2
3 f = open('log',mode='rb')
4 data = f.read()
5 f.close()
6
7 result = chardet.detect(open('log',mode='rb').read())
8 print(result)
输出:
1 {'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
四、循环文件
1 f = open("兼职白领学生空姐模特护士联系方式.txt",'r',encoding="utf-8")
2
3 for line in f:
4 print(line)
5
6 f.close()
输出即将文件内容循环输出
五、写文件
1 f = open('路径+文件名','r',encoding='utf-8') 如果不加文件名则是在默认路径上写文件
2 f.write('文件内容')
3 f.close
六、二进制模式写文件
1 f = open(file='D:/工作日常/兼职白领学生空姐模特护士联系方式.txt',mode='wb') 以2进制模式写
2 f.write('北大本科美国留学一次50,微信号:xxxxx'.encode('utf-8'))
3 f.close()
注意:
在文件以w或wb的模式打开文件的时候是只写模式,读不了文件内容,并且打开文件的同时或默认将原文件内容清空。原本有内容的或以w或者wb模式打开,原内容就会清空,而不是添加内容,相当于写新的文件。
七、追加文件
1 f = open("兼职白领学生空姐模特护士联系方式.txt",'a',encoding="gbk")
2
3 f.write("\n杜姗姗 北京 167 49 13324523342")
4 f.close()
同样追加有a和ab(2进制模式追加)两种模式,当文件以这两种模式打开时,则只能追加,写入的内容会在原内容尾部追加
八、读写模式
之前的模式都说只读、只写、只追加,这种就是又可以读又可以写的模式
1 f = open("兼职白领学生空姐模特护士联系方式.txt",'r+',encoding="gbk")
2 data = f.read() #可以读内容
3 print(data)
4 f.write("\nblack girl 河北 167 50 13542342233") #可以写
5 f.close()
这样write后面的内容写到了文件的追后面,也就是相当于追加模式下多了一个读文件。
还有一个和读写模式相对应的是写读模式,同样的道理,写读模式是先写后读,而读的时候会把原文件清空,相当于只写模式,它只是多了一个读的功能,还是读后面写的内容,原先内容还读不出来,并没什么卵用,这里就不介绍了。
九、文件操作的其他功能
1 def flush(self, *args, **kwargs): # real signature unknown 2 把文件从内存buffer里强制刷新到硬盘
文件操作里,因为硬盘比内存慢很多,如果每次写的东西都从内存传到硬盘的话速度会很慢,所以你写的内容都先存到内存buffer里,当buffer慢了会自动写到硬盘里,或者直到你f.close 后就会把buffer里的内容传到硬盘里。
它的作用:可以让你手动强制把内存buffer里内容刷新到硬盘
用法:f.flush()
1 def readline(self, *args, **kwargs): # real signature unknown 2 只读一行,遇到\r or \n为止
f.read()是把文件全部读完,f.readline()则是读到\n也就是换行的时候停止,这个可以结合for循环对文件循环的时候用
用法: f.readline()
1 def tell(self, *args, **kwargs): # real signature unknown 2 返回当前文件操作光标位置
f.tell() 系统会返回当前文件操作的光标位置
1 def seek(self, *args, **kwargs): # real signature unknown
2 把操作文件的光标移到指定位置
3 *注意seek的长度是按字节算的, 字符编码存每个字符所占的字节长度不一样。
4 如“路飞学城” 用gbk存是2个字节一个字,用utf-8就是3个字节,因此以gbk打开时,seek(4) 就把光标切换到了“飞”和“学”两个字中间。
5 但如果是utf8,seek(4)会导致,拿到了飞这个字的一部分字节,打印的话会报错,因为处理剩下的文本时发现用utf8处理不了了,因为编码对不上了。少了一个字节
注意:f.tell()和f.seek()都说按文件字节来算,所以要注意你的文件的编码格式,gbk中,中文是2个字节一中文,在utf-8中是3个字节一个中文,f.read()是按字符算的,括号里写3那就是读这个文件里第三个字符。
1 def truncate(self, *args, **kwargs): # real signature unknown
2 按指定长度截断文件
3 *指定长度的话,就从文件开头开始截断指定长度,不指定长度的话,就从当前位置到文件尾部的内容全去掉。
f.truncate()里面如果指定长度(也是指定字节)如:f.truncate(3)那就会从开头到字节3,后面的都全部去掉。
十、修改文件
1 import os
2
3 f_name = "兼职白领学生空姐模特护士联系方式utf8.txt"
4 f_new_name = "%s.new" % f_name
5
6 old_str = "乔亦菲"
7 new_str = "[乔亦菲 Yifei Qiao]"
8
9 f = open(f_name,'r',encoding="utf-8")
10 f_new = open(f_new_name,'w',encoding="utf-8")
11
12 for line in f:
13
14 if old_str in line:
15 new_line = line.replace(old_str,new_str)
16 else:
17 new_line = line
18
19 f_new.write(new_line)
20
21 f.close()
22 f_new.close()
23
24 os.rename(f_new_name,f_name) #把新文件名字改成原文件 的名字,就把之前的覆盖掉了,windows使用os.replace # 帮助文档说明replace会覆盖原文件
3.3、函数
一、函数的定义
定义:函数是指将一组语句的集合通过一个名字(函数名)封装起来,要想执行这个函数只需调用其函数名即可。
作用:减少重复的代码
使程序变的可扩展
使程序变得易维护
定义:在Python中,定义一个函数要使用def
语句,依次写出函数名、括号、括号中的参数和冒号:
,然后,在缩进块中编写函数体,函数的返回值用return
语句返回。
例子
1 def my_abs(x):
2 if x >= 0:
3 return x
4 else:
5 return -x
my_abs(3) 直接输入函数名带参数来执行函数
二、函数参数
函数参数分为
1、形式参数:只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。所以,形参之在函数内部有效。函数调用结束返回主调用函数后则不能再使用该形参。
2、实际参数:可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使参数获得确定值
这一块等后面学了作用域后再回过头来分析
三、默认参数
1 def stu_register(name,age,country,course):
2 print("----注册学生信息------")
3 print("姓名:",name)
4 print("age:",age)
5 print("国籍:",country)
6 print("课程:",course)
7
8 stu_register("王山炮",22,"CN","python_devops")
9 stu_register("张叫春",21,"CN","linux")
10 stu_register("刘老根",25,"CN","linux")
像coutry这个参数,在调用的时候大多数都是cn,这种情况下,我们可以在定义函数的时候给country设置一个默认参数,这样你在调用参数输入实参的时候,就不需要输入参数了,它会安装形参里定义的调用,当你不想按默认参数时,你按正常输入实参来输入你的参数即可。
1 def stu_register(name,age,course,country="CN"):
把函数的形参改成这样,你以后调用的时候不输入country这个实参,会默认给你传cn作为你的实参。
注意默认参数要写在位置参数后面
四、关键参数
调用函数输入实参的时候,必须按形参顺序来,如果不想按形参顺序来调用的话,就可以用关键参数,即指定参数名
还是上面的例子
1 stu_register("王山炮",course='PY', age=22,country='JP' )
可以这样调用,一定要写在位置参数后面。
五、非固定参数
1 def stu_register(name,age,*args): # *args 会把多传入的参数变成一个元组形式
2 print(name,age,args)
3
4 stu_register("Alex",22)
5 #输出
6 #Alex 22 () #后面这个()就是args,只是因为没传值,所以为空
7
8 stu_register("Jack",32,"CN","Python")
9 #输出
10 # Jack 32 ('CN', 'Python')
当我们不知道实参会传入多少个值的时候,在定义形参的时候就可以使用非固定参数(在不确定的形参前面加个*) 这样子在调用函数输入实参的时候你输入完name,age后剩下对应的就是args,此时你输入多少个值都不会报错,因为他对应的形参时*args,你输入的值会变成一个元祖的形式传入,输出就会像上面是一个('CN','python')。
还有一点需要注意的:当我们传入实参的时候,如果这个实参本身就是一个列表或者元祖的时候,比如lis = ['CN','python'],当我们传参安装 jack,27,lis的话,函数会把lis当成一个整体作为元祖的一个元素输出(输出结果为:jack,27,(['CN','python'],)但是我们像要的是lis里的每一个值为元素,那该怎么办?
这里我们就需要在调用函数传参的时候,在实参里传入lis的前面也加上一个*这样就会把lis里每一个元素作为元祖里每一个元素输出
输出结果:jack,27,('CN'),('python')
还有个**kwargs
1 def stu_register(name,age,*args,**kwargs): # *kwargs 会把多传入的参数变成一个dict形式
2 print(name,age,args,kwargs)
3
4 stu_register("Alex",22)
5 #输出
6 #Alex 22 () {}#后面这个{}就是kwargs,只是因为没传值,所以为空
7
8 stu_register("Jack",32,"CN","Python",sex="Male",province="ShanDong")
9 #输出
10 # Jack 32 ('CN', 'Python') {'province': 'ShanDong', 'sex': 'Male'}
**kwargs传参的时候需要安装字典的格式,key=valus 的形式去参数,这样输出的就是字典,道理和👆的一样,同样需要注意如果传参的本来就是个字典的话,在前面加上**即可。
六、返回值
函数外部的代码想要获取函数执行的结果,就可以在函数里用return语句把结果返回。
1 def stu_register(name, age, course='PY' ,country='CN'):
2 print("----注册学生信息------")
3 print("姓名:", name)
4 print("age:", age)
5 print("国籍:", country)
6 print("课程:", course)
7 if age > 22:
8 return False
9 else:
10 return True
11
12 registriation_status = stu_register("王山炮",22,course="PY全栈开发",country='JP')
13
14 if registriation_status:
15 print("注册成功")
16
17 else:
18 print("too old to be a student.")
需要我们注意的是:1、函数在执行过程中遇到return语句,就会停止执行过程并返回结果,函数遇到return就代表停止了函数。
2、如果函数里没有指定return,那这个函数会返回None
3、return只能返回一个值,如果你定义了返回多个值,它会以元祖的形式返回出来,定义列表也是返回一个列表。
七、全局与局部变量
全局变量:定义在函数外部的一级代码(没有缩进)的变量为全局变量,全局变量全局可用。
局部变量:定义在函数内部的变量为局部变量,只能在局部生效,函数一旦执行完毕,局部变量内存会清空,即调用不了,在函数内部可用调用外部即全局变量。
当全局和局部都有一个相同的变量时,函数查找变量的顺序是由内而外(也就是说先在内部找,找不到再去外部找),这里还有一点需要注意,当有多个相同的函数,也就是多个局部变量时,他们之间是不能互相调用的,所以函数查找变量的顺序还是先找自己,自己没有找全局变量。
八、作用域
一个函数就是一个作用域,函数一旦生成,作用域就成立
这里作用域没有细讲,结合上面的全局变量来说,当函数里想改变全局变量的时候需要用到global语法,后面跟需要修改的变量名。
这里需要注意一下,想字符串数字这种不变的全局变量必须global以后才能修改,但是像列表这种可变的(内部元素可用修改,因为其对应的内存地址)修改他其中的元素是不需要global的
九、嵌套函数
1 name = "Alex"
2
3 def change_name():
4 name = "Alex2"
5
6 def change_name2():
7 name = "Alex3"
8 print("第3层打印", name)
9
10 change_name2() # 调用内层函数
11 print("第2层打印", name)
12
13
14 change_name()
15 print("最外层打印", name)
嵌套函数即函数里套着函数
嵌套函数这里需要注意的是,代码从上到下来执行,在遇到嵌套函数的时候只是把函数保存在内存里,没有调用,就不会执行函数。结合这两点,无论变量在哪个位置出现都可以分析出来
十、匿名函数
匿名函数就是不需要显示的指定函数名
1 #这段代码
2 def calc(x,y):
3 return x**y
4
5 print(calc(2,5))
6
7 #换成匿名函数
8 calc = lambda x,y:x**y
9 print(calc(2,5))
一般来说可以和后面的函数内置方法搭配的使用
1 res = map(lambda x:x**2,[1,5,7,4,8])
2 for i in res:
3 print(i)
4
5 和map方法搭配使用
匿名函数的作业:节省代码量
十一、高阶函数
1 def add(x,y,f):
2 return f(x) + f(y)
3
4
5 res = add(3,-6,abs)
6 print(res)
高阶函数:接受一个或多个函数作为参数就是高阶函数
或者 return返回了另外一个函数也叫作高阶函数
十二、递归函数
一个函数在内部调用自身这个函数,那这个函数就是递归函数。
特性:
1、递归函数必须有一个限制条件(也就是有结束条件),不然它会无限循环,直到栈溢出。
2、每次进入一层递归是,你需要解决的问题相比上一次递归会有所减少,也就是值你的函数起到了作用,没起一次作用离你的目标就越近。
1 def calc(n):
2 v = int(n/2)
3 print(v)
4 if v > 0:
5 calc(v)
6 print(n)
7
8 calc(10)
还有一点需要注意的是,每次递归遇到调用自己的函数就进入了一层递归,但是之前的函数还没执行完成,就会卡在那里,等到最后一次递归得到目标后,剩下的函数会继续走完。
十三、内置函数
1、abs() #取绝对值
2、dict() #把数据转为字典
3、min() #取数据里最小值
1 a = [8,2,5,6,9,1]
2 min(a)
3
4 >>>1
4、max() #取数据里最大值
1 a = [8,2,5,6,9,1]
2 min(a)
3
4 >>>9
5、help() #查看函数用法
6、bool() #判断数字是否为True,只有0会返回False,其他的都是返回True
7、all() # 判断数据列表里是否有0,数据里False就不会返回True,只有全部都是True才会返回True。 有0返回False,其他是True,包括空列表
8、any() #列表里只要有一个True,就会返回Ture
9、dir() #打印当前程序的所有变量
1 >>> dir()
2 ['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'goods']
3
4 前面带__线的是系统变量,后面的是之前定义的变量
10、hex() #把数字转成16进制
1 >>> hex(13)
2 '0xd'
3 >>> hex(14)
4 '0xe'
5 >>> hex(1407)
6 '0x57f'
7 >>>
11、divmod() #返回整除数和余数
1 >>> divmod(10,3)
2 (3, 1)
3 >>> divmod(16,6)
4 (2, 4)
12、sorted() #按顺序来排序
13、oct() #把10进制转换成8进制
14、bin() #转换成2进制
15、exec() #和eval()效果一样,区别在于eval只能转换单行代码,exec可以转换多行代码,但是exec拿不到返回值
16、ord() 和 chr()是相对于的 根据ascii码对应关系返回
1 >>> ord('a')
2 97
3 >>> chr(97)
4 'a'
5 >>> ord('b')
6 98
7 >>> chr(98)
17、sum() #将列表里所有数字求和
1 >>> a = [1,-5,6,3,8,9]
2 >>> sum(a)
3 22
18、bytearray()字符串是不能被修改的,使用这个函数可以在原内存地址上修改字符串而不是重新赋值
1 >>>s = 'abc路飞'
2 >>> s = s.encode() #在使用bytearray前要先encode一下,转成bytes类型
3 >>> s
4 b'abc\xe8\xb7\xaf\xe9\xa3\x9e'
5 >>> s[0] =65
6 >>> s
7 bytearray(b'Abc\xe8\xb7\xaf\xe9\xa3\x9e') #修改后的字符串
8 >>> s.decode() # 再转换回来
9 'Abc路飞' #可以看到原字符串的a改变成了A
用于大字符串修改的时候,就不会占内存
19、 map()
1 >>> list(map(lambda x:x*x,[1,2,3,4,5]))
2 [1, 4, 9, 16, 25]
3 >>>
4
5 map,让括号后面的按照前面的规则前执行,前面跟一个匿名函数,这个函数定义了x*x所有作用在后面这个列表里,返回一个每个元素相乘的列表
20、 filter()
1 >>> list(filter(lambda x:x>2,[1,2,3,4,5]))
2 [3, 4, 5]
3
4 filter是过滤的意思,按照前面的匿名函数的规则把符合规则的都返回出来,过滤掉不符合规则的。
21、reduce() python3中没有这个功能了,需要导入一个模块
1 >>> import functools
2 >>> functools.reduce(lambda x,y:x+y,[1,2,3,4,5])
3 15
4 >>> functools.reduce(lambda x,y:x*y,[1,2,3,4,5])
5 120
6 >>>
7
8 他也是按照匿名函数的规则去执行,不但可以两两相加还可以相乘
22、pow() #传入两个数字,返回第一个数字的第二个数字次的幂
1 >>> pow(5,2) #5的2次幂 2 25 3 >>> pow(3,3) #3的3次幂 4 27 5 >>>
23、print()
>>> a = """
... 来试一试
... print
... 的
... """
>>> print(a )
来试一试
print
的
>>> print(a,end="*")
来试一试
print
的
*>>>
# print默认会在最后给你加一个\n 也就是自动换行,打印完后就跳到下一行
#当里面声明end=''后 他最后会按你的指定来加上,而不是自动加上\n,也就不会自动换行
>>> print('crazy_heng','mengmeng')
crazy_heng mengmeng
>>> print('crazy_heng','mengmeng',sep='->')
crazy_heng->mengmeng
在打印多个字符串时,原本print会自动在中间加上空格,当你指定sep= 以后,print会按你指定的加上。
24、callable() #括号里加上数据类型如:函数或者列表。 判断括号里的数据能不能被调用,能调用返回Ture否则返回False
25、vars() #打印所有的变量名和变量值,和dir()的区别在于dir只打印变量名,vars打印变量名和变量值
26、locals()
1 >>> def func():
2 ... n = 3
3 ... n1 =4
4 ... print(locals())
5 ...
6 >>>
7 >>> func()
8 {'n1': 4, 'n': 3}
9
10 locals 函数里的局部变量
27、zip()
1 >>> a = [1,2,3,4,5]
2 >>> b = ['a','b','c']
3 >>> list(zip(a,b))
4 [(1, 'a'), (2, 'b'), (3, 'c')]
5
6 将两个列表一一对应,没有的丢弃
28、round()
1 >>> round(3.5453452)
2 4
3 >>> round(3.5453452,2)
4 3.55
5 >>>
6
7
8 保留指定的几位小数,没有指定就四舍五入
29、set() 括号里输入列表,返回集合
3.4、函数进阶
一、命名空间
有一个 x = 1
这两个值都是存在内存里,x 这个变量名和 1的对应关系都存在名词空间里
名称空间共3种:
1 locals: 是函数内的名称空间,包括局部变量和形参 #打印当前所以环境的名称空间
2 globals: 全局变量,函数定义所在模块的名字空间
3 builtins: 内置模块的名字空间
作用域的查找顺序:LEGB
L:locals 是函数内的名字空间,包括局部变量和形参
E:enclosing 外部嵌套函数的名字空间
G:globals 全局变量,函数定义所在模块的名字空间
B:builtins 内置模块的名字空间
二、闭包
1 def outer():
2 name = 'alex'
3
4 def inner():
5 print("在inner里打印外层函数的变量",name)
6
7 return inner
8
9
10 f = outer()
11
12 f()
在上面这种情况下,按正常情况下是函数执行完就会释放内容,里的的作用域就不存在了,但是当函数返回了一个它的内容函数,这个内部函数还使用了它外部函数的参数,相当于使用了外部函数的作用域,那无论何时我们调用函数,他的作用域都有用,没有被释放。这种现象就是闭包。
三、装饰器
1 #_*_coding:utf-8_*_
2
3 user_status = False #用户登录了就把这个改成True
4
5 def login(func): #把要执行的模块从这里传进来
6
7 def inner(*args,**kwargs):#再定义一层函数
8 _username = "alex" #假装这是DB里存的用户信息
9 _password = "abc!23" #假装这是DB里存的用户信息
10 global user_status
11
12 if user_status == False:
13 username = input("user:")
14 password = input("pasword:")
15
16 if username == _username and password == _password:
17 print("welcome login....")
18 user_status = True
19 else:
20 print("wrong username or password!")
21
22 if user_status == True:
23 func(*args,**kwargs) # 看这里看这里,只要验证通过了,就调用相应功能
24
25 return inner #用户调用login时,只会返回inner的内存地址,下次再调用时加上()才会执行inner函数
26
27
28 def home():
29 print("---首页----")
30
31 @login
32 def america():
33 #login() #执行前加上验证
34 print("----欧美专区----")
35
36 def japan():
37 print("----日韩专区----")
38
39 # @login
40 def henan(style):
41 '''
42 :param style: 喜欢看什么类型的,就传进来
43 :return:
44 '''
45 #login() #执行前加上验证
46 print("----河南专区----")
47
48 home()
49 # america = login(america) #你在这里相当于把america这个函数替换了
50 henan = login(henan)
51
52 # #那用户调用时依然写
53 america()
54
55 henan("3p")
四、生成器&迭代器
函数里有yield 你在外部调用函数只会生出一个生成器,然后next()函数以后 yield会把每一次的执行结果返回给外面。 这样你就能拿到函数里每一步的执行结果
1、通过类时列表生成式的东西来生成(列表生成式 a = [i for i in range(10)]
2、可以通过函数里加上yield来生成
yield
在函数里,遇到yield那这个函数就是生成器,当执行这个函数的时候,就生成了一个生成器,函数内部遇到yield就会停止,并向函数外部返回值,然后等待下一次next()调用。
调用:
执行函数后,next()加上生成器,就可以调用生成器,函数内部执行函数直到遇到yield,并向外面返回值,然后函数等待外部再次调用next()加函数,
send():
函数外部可以向函数发消息,直接用生成器.send(发送的内容) 这样函数内容可以接收信息。
迭代去:
可迭代对象(Iterable):可以用来for循环的对象,都是可迭代对象
isinstance()方法:可以利用这个方法判断是不是可迭代对象
迭代器:
可以被next()函数调用并不断返回下一个值的对象称为迭代器Iterator
生成器就是一种迭代器
总结:
凡事可以for循环的,都是Iterable类型;
凡事可以next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列