今天学习的内容如下:
1.协程函数
2.递归
3.匿名函数lambda
4.内置函数(map,reduce,filter,max,min,zip,sorted)
5.面向过程编程与函数编程
6.模块与包的使用
7.常用模块
下面开始上节课的复习:
1.函数对象(第一类对象):
1.1 函数可以被当成数据来传递
1.2 可以被引用
1.3 可以当作参数传递给另一个函数
1.4 可以当作函数的返回值
1.5 可以当作容器类型的元素
2.函数的嵌套
2.1 函数的嵌套调用(在调用函数的过程中 有调用了其他的函数)
2.2 函数的嵌套定义(在定义函数时,又实用def关键字定义了其他函数)
3.名称空间和作用域
3.1内置名称空间(变量的定义 都是绑定操作,都没有储值的操作)(python解释器启动的时候产生)
3.2 全局名称空间(文件级别的)(打开文件的时候产生)
3.3 局部名称空间(函数内部定义的名字)(调用函数的时候产生)
3.4 全局作用域(全局名称空间和内置名称空间)
3.5 局部作用域(局部名称空间)
4 闭包
4.1 定义在函数内部的函数
4.2 该内部函数包含对外部作用域而不是对全局作用域的引用
可以用来惰性计算
5. 装饰器
开放封闭原则,对修改是封闭的,对扩展是开放的
装饰器可以是任意可调用的对象
装饰器并不仅限于函数
被装饰的对象也是任意可调用对象
装饰器遵循的原则:
不修改被装饰对象的源代码,不修改被装饰对象的调用方式
6 迭代器
迭代重复上次的过程,每一次迭代都是基于上一次迭代的结果继续进行
可迭代对象:obj__iter__方法
迭代器对象:obj__iter__ obj__next__方法
提供了一种不需要依赖于索引的迭代方式
for 循环的原理是 首先执行iter方法,然后用next方法去得到
迭代器的优点和缺点:
提供了一种不依赖于索引的迭代方式
节省内存
缺点:
无法获取长度
一次性的
7 生成器
函数体内包含yield,函数的执行结果是生成器对象,本质是迭代器,把函数的执行结果做成了迭代器
返回多次值,return只能返回一次值
可以暂停 挂起函数的执行,下一次 基于上次的执行状态继续执行
8 内置函数
今天上课的内容
1.yield的语句形式: yield 1
2.yield的表达式形式: x=yield(额外的功能,传参数)
例子:
1 def eater(name): 2 print('%s ready to eat' %(name)) 3 while True: 4 food = yield 5 print('%s start to eat %s' %(name,food)) 6 7 g = eater('alex') 8 next(g) 9 10 g.send('手指头') 11 g.send('脚趾头')
3. 加一个装饰器,实现初始化的功能
1 def deco(func): 2 def wrapper(*args,**kwargs): 3 res = func(*args,**kwargs) 4 next(res) 5 return res 6 return wrapper 7 8 @deco 9 def eater(name): 10 print('%s ready to eat' %(name)) 11 while True: 12 food = yield 13 print('% start eat %s' %(name,food))
4. 模拟菜单功能
1 def deco(func): 2 def wrapper(*args,**kwargs): 3 res = func(*args,**kwargs) 4 next(res) 5 return res 6 return wrapper 7 8 @deco 9 def eater(name): 10 print('%s ready to eat' %(name)) 11 food_list = [] 12 while True: 13 food = yield food_list 14 food_list.append(food) 15 print('%s start eat %s' %(name,food)) 16 17 g = eater('alex') 18 print(g.send('egon1')) 19 print(g.send('egon2'))
5. x=yield的功能
5.1 g.send('111'),先把111传值给yield,由yield赋值给x,然后再往下执行,直到再次碰到yield,然后把yield后的返回值返回
6. yield的应用
例如:grep -rl 'python' /root(找到/root目录下所有文件中包含python内容的文件,把文件名返回)
1 import os 2 3 def deco(func): 4 def wrapper(*args,**kwargs): 5 res = func(*args,**kwargs) 6 next(res) 7 return res 8 return wrapper 9 10 @deco 11 def search(target): 12 while True: 13 serach_path = yield 14 g=os.walk(serach_path) 15 for par_dir,_,files in g: 16 for file in files: 17 file_abs_path=r'%s\%s' %(par_dir,file) 18 # print(file_abs_path) 19 target.send(file_abs_path) 20 21 22 @deco 23 def opener(target): 24 while True: 25 file_abs_path=yield 26 with open(file_abs_path,encoding='utf-8') as f: 27 target.send((file_abs_path,f)) 28 @deco 29 def cat(target): 30 while True: 31 file_abs_path,f = yield 32 for line in f: 33 target.send((file_abs_path,line)) 34 @deco 35 def grep(target,pattern): 36 while True: 37 line = yield 38 if pattern in line: 39 target.send(file_abs_path) 40 41 @deco 42 def printer(): 43 while True: 44 45 file_abs_path = yield 46 print(file_abs_path) 47 48 g = search(opener(cat(grep(printer),'python'))) 49 g.send('/root')
7. 面向过程的程序设计
是一种流水线式的编程思路,是机械式的,优点是:程序的结构清晰,可以把复杂的问题简单化,明细化。缺点是:扩展性差
应用场景:linux的内核,git,httpd都是面向过程方式写的
8. 模块与包
什么模块,一个py文件 就是一个模块,一个模块就是一个包含了python定义和申明的文件。
为什么要使用模块:方便管理,实现了功能的重复利用
引用的两种方式
import ...
from ... import ...(优点:不用加前缀,缺点:容易和当前的重名)
导入模块干的事:
1.产生新的名称空间
2.以新建的名称空间为全局名称空间,执行文件的代码
3.拿到一个模块名,指向导入的模块.py产生的名称空间
spam.money(引用模块的值)
spam.read1(拿到read1的内存地址,然后可以执行)
import ... as ... (起别名,一行也可以导入多个,通过逗号隔开)
从哪个文件来就以哪个文件的环境变量为准
__all__=[](和from import * 一起使用,列表里面必须是字符串,控制导入的值)
9. __name__
__file__:绝对路径
1.当作脚本执行的时候__name__ == '__main__'
2.当作模块导入: __name__ == 模块名
1 if __name == '__main__': 2 print('当作脚本执行')
10. 模块的搜索路径
先从内存里面找,然后去内置的里面找,然后去sys.path里面去找(sys.path,就是一个列表)
11. 包
1. 无论是import形式还是from...import形式,凡是再导入语句中(而不是在使用时)遇到带点的,都要第一时间提高警觉,这都是关于包才有的导入语法
2. 包是目录级别的(文件夹级),文件夹是用来组成py文件的(包的本质就是一个包含__init__.py文件的目录)
3. import导入文件的时候,产生的名称空间中的名字来源于文件,import 包,产生的名称空间中的名字同样来源于文件,即包下的__init__.py,导入包本质就是在导入该文件
ps:包A和包B下有同名的模块也不会冲突,如A.a和B.a来自于两个命名空间
11.1 注意事项
1.关于包相关的导入语句也分为import和from ... import ...两种,但是无论哪种,无论在什么位置,在导入时都必须遵循一个原则:凡是在导入时带点的,点的左边都必须是一个包,否则非法。可以带有一连串的点,如item.subitem.subsubitem,但都必须遵循这个原则。
2.对于导入后,在使用时就没有这种限制了,点的左边可以是包,模块,函数,类(它们都可以用点的方式调用自己的属性)。
3.对比import item 和from item import name的应用场景:
如果我们想直接使用name那必须使用后者
11.2 __init__.py
不管是哪种方式,只要是第一次导入包或者是包的任何其他部分,都会依次执行包下的__init__.py文件(我们可以在每个包的文件内都打印一行内容来验证一下),这个文件可以为空,但是也可以存放一些初始化包的代码。
11.3 绝对导入和相对导入
我们的最顶级包glance是写给别人用的,然后在glance包内部也会有彼此之间互相导入的需求,这时候就有绝对导入和相对导入两种方式:
绝对导入:以glance作为起始
相对导入:用.或者..的方式最为起始(只能在一个包中使用,不能用于不同目录内)
例如:我们在glance/api/version.py中想要导入glance/cmd/manage.py
注意:在使用pycharm时,有的情况会为你多做一些事情,这是软件相关的东西,会影响你对模块导入的理解,因而在测试时,一定要回到命令行去执行,模拟我们生产环境,你总不能拿着pycharm去上线代码吧!!!
特别需要注意的是:可以用import导入内置或者第三方模块(已经在sys.path中),但是要绝对避免使用import来导入自定义包的子模块(没有在sys.path中),应该使用from... import ...的绝对或者相对导入,且包的相对导入只能用from的形式。
比如我们想在glance/api/versions.py中导入glance/api/policy.py,有的同学一抽这俩模块是在同一个目录下,十分开心的就去做了,它直接这么做
1 #在version.py中 2 3 import policy 4 policy.get()
没错,我们单独运行version.py是一点问题没有的,运行version.py的路径搜索就是从当前路径开始的,于是在导入policy时能在当前目录下找到
但是你想啊,你子包中的模块version.py极有可能是被一个glance包同一级别的其他文件导入,比如我们在于glance同级下的一个test.py文件中导入version.py,如下
1 from glance.api import versions 2 3 ''' 4 执行结果: 5 ImportError: No module named 'policy' 6 ''' 7 8 ''' 9 分析: 10 此时我们导入versions在versions.py中执行 11 import policy需要找从sys.path也就是从当前目录找policy.py, 12 这必然是找不到的 13 '''