第三篇函数
目录
高阶函数: 函数接收的参数是一个函数名,函数的返回值是一个函数名(满足其一)
函数嵌套:函数内部调用函数。
闭包:内部函数包含对外部作用域而非全局作用域的引用,闭包的意义:返回的函 数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,
该函数无论在何处调用,优先使用自己外层包裹的作用域 装饰器@ 装饰器=高阶函数+函数嵌套+闭包 不改变原来函数功能及代码,不改原来调用方式添加新功能。 迭代器iter():迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果 都是下一次迭代的初始值 迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象 可迭代对象执行obj.__iter__()得到的结果就是迭代器对象 而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象 生成器yield:只要函数内部包含有yield关键字,那么函数名()的结果对象就是生成 器 三元表达式 res='SB' if name == 'alex' else 'NB' 列表推导式 variable = [out_exp_res for out_exp in input_list if out_exp == 2] 生成器表达式: 把列表推导式的[]换成()就是生成器表达式 匿名函数lamdba x:x+1 func=lambda x,y,z=1:x+y+z func(1,2,3) 函数递归:递归调用应该包含两个明确的阶段:回溯,递推 内置函数
三、函数嵌套、名称空间与作用域、闭包、装饰器@ 。
#装饰器作用:不改变原来函数功能及代码,不改原来调用方式添加新功能。
然后在对象上面加上@装饰器名,装饰器=高阶函数+函数嵌套+[闭包]
装饰器参考https://www.cnblogs.com/wang-sir/articles/9282871.html
四、迭代器iter()、生成器yield(生成器就是迭代器) #迭代器作用:
五、三元表达式、列表推导式、生成器表达式、递归、匿名函数lamdba、内置函数
1 如何自定义函数
2 如何调用函数
函数的返回值
函数参数的应用:形参和实参,位置参数,关键字参数,默认参数,*args,**kwargs
3 高阶函数(函数对象)
4 函数嵌套
5 作用域与名称空间
6 装饰器
7 迭代器与生成器及协程函数
8 三元运算,列表解析、生成器表达式
9 函数的递归调用
10 内置函数
二 函数基础
1) 为何要用函数之不用函数的问题
#1、代码的组织结构不清晰,可读性差 #2、遇到重复的功能只能重复编写实现代码,代码冗余 #3、功能需要扩展时,需要找出所有实现该功能的地方修改之,无法统一管理且维护难度极大
2) 函数分类
#1、内置函数 为了方便我们的开发,针对一些简单的功能,python解释器已经为我们定义好了的函数即内置函数。对于内置函数,我们可以拿来就用而无需事先定义,如len(),sum() ps:我们将会在最后详细介绍常用的内置函数。 #2、自定义函数 很明显内置函数所能提供的功能是有限的,这就需要我们自己根据需求,事先定制好我们自己的函数来实现某种功能,以后,在遇到应用场景时,调用自定义的函数即可。例如
3)自定义函数
#语法
def 函数名(参数1,参数2,参数3,...):
'''注释'''
函数体
return 返回的值
#函数名要能反映其意义
def auth(user:str,password:str)->int: ''' auth function :param user: 用户名 :param password: 密码 :return: 认证结果 ''' if user == 'egon' and password == '123': return 1 # print(auth.__annotations__) #{'user': <class 'str'>, 'password': <class 'str'>, 'return': <class 'int'>} user=input('用户名>>: ').strip() pwd=input('密码>>: ').strip() res=auth(user,pwd) print(res)
4)函数使用的原则:先定义,再调用
函数在定义阶段都干了哪些事?
#只检测语法,不执行代码 也就说,语法错误在函数定义阶段就会检测出来,而代码的逻辑错误只有在执行时才会知道
5) 定义函数的三种形式
#1、无参:应用场景仅仅只是执行一些操作,比如与用户交互,打印 #2、有参:需要根据外部传进来的参数,才能执行相应的逻辑,比如统计长度,求最大值最小值 #3、空函数:设计代码结构
#4、是否有返回值
#定义阶段 def tell_tag(tag,n): #有参数 print(tag*n) def tell_msg(): #无参数 print('hello world') #调用阶段 tell_tag('*',12) tell_msg() tell_tag('*',12) ''' ************ hello world ************ ''' #结论: #1、定义时无参,意味着调用时也无需传入参数 #2、定义时有参,意味着调用时则必须传入参数
1 def auth(user,password): 2 ''' 3 auth function 4 :param user: 用户名 5 :param password: 密码 6 :return: 认证结果 7 ''' 8 pass 9 10 def get(filename): 11 ''' 12 :param filename: 13 :return: 14 ''' 15 pass 16 17 def put(filename): 18 ''' 19 :param filename: 20 :return: 21 ''' 22 def ls(dirname): 23 ''' 24 :param dirname: 25 :return: 26 ''' 27 pass 28 29 #程序的体系结构立见
6)调用函数
函数的调用:函数名加括号 1 先找到名字 2 根据名字调用代码
7) 函数返回值
无return->None return 1个值->返回1个值 return 逗号分隔多个值->元组
什么时候该有返回值? 调用函数,经过一系列的操作,最后要拿到一个明确的结果,则必须要有返回值 通常有参函数需要有返回值,输入参数,经过计算,得到一个最终的结果 什么时候不需要有返回值? 调用函数,仅仅只是执行一系列的操作,最后不需要得到什么结果,则无需有返回值 通常无参函数不需要有返回值
8) 函数调用的三种形式
1 语句形式:foo()
2 表达式形式:3*len('hello')
3 当中另外一个函数的参数:range(len('hello'))
9) 函数的参数
形参与实参:
#形参即变量名,实参即变量值,函数调用时,将值绑定到变量名上,函数调用结束,解除绑定
def foo(x,y,*args,**kwargs):
#位置参数都放在左边,*args是剩余的位置参数都存放在里面,**kwatgs里面都存放c=4,d=5
使用时可以foo(x,y,*[a,c,d],**{w,q,o})
args接收的是元组
kwargs接收的是字典
具体应用:
#1、位置参数:按照从左到右的顺序定义的参数 位置形参:必选参数 位置实参:按照位置给形参传值 #2、关键字参数:按照key=value的形式定义的实参 无需按照位置为形参传值 注意的问题: 1. 关键字实参必须在位置实参右面 2. 对同一个形参不能重复传值 #3、默认参数:形参在定义时就已经为其赋值 可以传值也可以不传值,经常需要变得参数定义成位置形参,变化较小的参数定义成默认参数(形参) 注意的问题: 1. 只在定义时赋值一次 2. 默认参数的定义应该在位置形参右面 3. 默认参数通常应该定义成不可变类型 #4、可变长参数: 可变长指的是实参值的个数不固定 而实参有按位置和按关键字两种形式定义,针对这两种形式的可变长,形参对应有两种解决方案来完整地存放它们,分别是*args,**kwargs ===========*args=========== def foo(x,y,*args): print(x,y) print(args) foo(1,2,3,4,5) def foo(x,y,*args): print(x,y) print(args) foo(1,2,*[3,4,5]) def foo(x,y,z): print(x,y,z) foo(*[1,2,3]) ===========**kwargs=========== def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,a=1,b=2,c=3) def foo(x,y,**kwargs): print(x,y) print(kwargs) foo(1,y=2,**{'a':1,'b':2,'c':3}) def foo(x,y,z): print(x,y,z) foo(**{'z':1,'x':2,'y':3}) ===========*args+**kwargs=========== def foo(x,y): print(x,y) def wrapper(*args,**kwargs): print('====>') foo(*args,**kwargs) #5、命名关键字参数:*后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递 可以保证,传入的参数中一定包含某些关键字 def foo(x,y,*args,a=1,b,**kwargs): print(x,y) print(args) print(a) print(b) print(kwargs) foo(1,2,3,4,5,b=3,c=4,d=5) 结果: 1 2 (3, 4, 5) 1 3 {'c': 4, 'd': 5}
10) 练习
1、写函数,,用户传入修改的文件名,与要修改的内容,执行函数,完成批了修改操作 2、写函数,计算传入字符串中【数字】、【字母】、【空格] 以及 【其他】的个数 3、写函数,判断用户传入的对象(字符串、列表、元组)长度是否大于5。 4、写函数,检查传入列表的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。 5、写函数,检查获取传入列表或元组对象的所有奇数位索引对应的元素,并将其作为新列表返回给调用者。 6、写函数,检查字典的每一个value的长度,如果大于2,那么仅保留前两个长度的内容,并将新内容返回给调用者。 dic = {"k1": "v1v1", "k2": [11,22,33,44]} PS:字典中的value只能是字符串或列表
1 #题目一 2 def modify_file(filename,old,new): 3 import os 4 with open(filename,'r',encoding='utf-8') as read_f,\ 5 open('.bak.swap','w',encoding='utf-8') as write_f: 6 for line in read_f: 7 if old in line: 8 line=line.replace(old,new) 9 write_f.write(line) 10 os.remove(filename) 11 os.rename('.bak.swap',filename) 12 13 modify_file('/Users/jieli/PycharmProjects/爬虫/a.txt','alex','SB') 14 15 #题目二 16 def check_str(msg): 17 res={ 18 'num':0, 19 'string':0, 20 'space':0, 21 'other':0, 22 } 23 for s in msg: 24 if s.isdigit(): 25 res['num']+=1 26 elif s.isalpha(): 27 res['string']+=1 28 elif s.isspace(): 29 res['space']+=1 30 else: 31 res['other']+=1 32 return res 33 34 res=check_str('hello name:aSB passowrd:alex3714') 35 print(res) 36 37 38 #题目三:略 39 40 #题目四 41 def func1(seq): 42 if len(seq) > 2: 43 seq=seq[0:2] 44 return seq 45 print(func1([1,2,3,4])) 46 47 48 #题目五 49 def func2(seq): 50 return seq[::2] 51 print(func2([1,2,3,4,5,6,7])) 52 53 54 #题目六 55 def func3(dic): 56 d={} 57 for k,v in dic.items(): 58 if len(v) > 2: 59 d[k]=v[0:2] 60 return d 61 print(func3({'k1':'abcdef','k2':[1,2,3,4],'k3':('a','b','c')}))
三 函数对象、函数嵌套、名称空间与作用域、装饰器
1) 函数是第一类对象,即函数可以当作数据传递
#1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #3 可以当作容器类型的元素
2)利用该特性,优雅的取代多分支的if
1 def foo(): 2 print('foo') 3 4 def bar(): 5 print('bar') 6 7 dic={ 8 'foo':foo, 9 'bar':bar, 10 } 11 while True: 12 choice=input('>>: ').strip() 13 if choice in dic: 14 dic[choice]()
3)函数的嵌套调用
def max(x,y): return x if x > y else y def max4(a,b,c,d): res1=max(a,b) res2=max(res1,c) res3=max(res2,d) return res3 print(max4(1,2,3,4))
4)函数的嵌套定义
def f1(): def f2(): def f3(): print('from f3') f3() f2() f1() f3() #报错,为何?请看下一小节
5)命名空间与作用域
《一》 什么是命名空间?
#命名空间:存放名字的地方,三种名称空间,(x=1,1存放于内存中,那名字x存放在哪里呢?命名空间正是存放名字x与1绑定关系的地方)
《二》命名空间的加载顺序
python test.py #1、python解释器先启动,因而首先加载的是:内置命名空间 #2、执行test.py文件,然后以文件为基础,加载全局命名空间 #3、在执行文件的过程中如果调用函数,则临时产生局部命名空间
《三》 名字的查找顺序
局部名称空间--->全局名称空间--->内置名称空间 #需要注意的是:在全局无法查看局部的,在局部可以查看全局的,如下示例 # max=1 def f1(): # max=2 def f2(): # max=3 print(max) f2() f1() print(max)
作用域
#1、作用域即范围 - 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效 - 局部范围(局部名称空间属于该范围):临时存活,局部有效 #2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下 x=1 def f1(): def f2(): print(x) return f2 x=100 def f3(func): x=2 func() x=10000 f3(f1()) #3、查看作用域:globals(),locals() LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__ locals 是函数内的名字空间,包括局部变量和形参 enclosing 外部嵌套函数的名字空间(闭包中常见) globals 全局变量,函数定义所在模块的名字空间 builtins 内置模块的名字空间
6) global与nonlocal关键字
global适用于函数内部修改全局变量的值
nonlocal适用于嵌套函数中内部函数修改外部变量的值
*****************************************************************************************************
7) 闭包函数
#内部函数包含对外部作用域而非全局作用域的引用 #提示:之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路,包起来喽,包起呦,包起来哇 def counter(): n=0 def incr(): nonlocal n x=n n+=1 return x return incr c=counter() print(c()) print(c()) print(c()) print(c.__closure__[0].cell_contents) #查看闭包的元素
二 闭包的意义与应用
#闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域 #应用领域:延迟计算(原来我们是传参,现在我们是包起来) from urllib.request import urlopen def index(url): def get(): return urlopen(url).read() return get baidu=index('http://www.baidu.com') print(baidu().decode('utf-8'))
8)装饰器 参考https://www.cnblogs.com/wang-sir/articles/9282871.html
装饰器=高阶函数+函数嵌套+闭包。
何为高阶函数?满足如下其一便是。
函数接收的参数是一个函数名 #传进去后里面便可以调用此函数。
函数的返回值是一个函数名
满足第一条可以实现不改变原来函数,但调用方式需要改变。
满足第二条,返回一个函数名,那么赋给一个变量b,b表示一个内存地址,需要b()就可以当函数处理了。
def foo(): print('我的函数名作为参数传给高阶函数') def gao_jie1(func): print('我就是高阶函数1,我接收的参数名是%s' %func) func() def gao_jie2(func): print('我就是高阶函数2,我的返回值是%s' %func) return func gao_jie1(foo) gao_jie2(foo)
#高阶函数应用1:把函数当做参数传给高阶函数 import time def foo(): print('from the foo') def timmer(func): start_time=time.time() func() stop_time=time.time() print('函数%s 运行时间是%s' %(func,stop_time-start_time)) timmer(foo) #总结:我们确实为函数foo增加了foo运行时间的功能,但是foo原来的执行方式是foo(),现在我们需要调用高阶函数timmer(foo),改变了函数的调用方式
#高阶函数应用2:把函数名当做参数传给高阶函数,高阶函数直接返回函数名 import time def foo(): print('from the foo') def timmer(func): start_time=time.time() return func stop_time=time.time() print('函数%s 运行时间是%s' %(func,stop_time-start_time)) foo=timmer(foo) foo() #总结:我们确实没有改变foo的调用方式,但是我们也没有为foo增加任何新功能
什么是装饰器?
装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。 装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式 装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能
装饰器作用:不改变原来函数功能及代码,不改原来调用方式添加新功能。
然后在对象上面加上@装饰器名。
装饰器的使用
1 import time 2 def timmer(func): 3 def wrapper(*args,**kwargs): 4 start_time=time.time() 5 res=func(*args,**kwargs) 6 stop_time=time.time() 7 print('run time is %s' %(stop_time-start_time)) 8 return res 9 return wrapper 10 11 @timmer 12 def foo(): 13 time.sleep(3) 14 print('from foo') 15 foo()
1 def auth(driver='file'): 2 def auth2(func): 3 def wrapper(*args,**kwargs): 4 name=input("user: ") 5 pwd=input("pwd: ") 6 7 if driver == 'file': 8 if name == 'egon' and pwd == '123': 9 print('login successful') 10 res=func(*args,**kwargs) 11 return res 12 elif driver == 'ldap': 13 print('ldap') 14 return wrapper 15 return auth2 16 17 @auth(driver='file') 18 def foo(name): 19 print(name) 20 21 foo('egon')
装饰器语法
被装饰函数的正上方,单独一行 @deco1 @deco2 @deco3 def foo(): pass foo=deco1(deco2(deco3(foo)))
装饰器补充:wraps
from functools import wraps def deco(func): @wraps(func) #加在最内层函数正上方 def wrapper(*args,**kwargs): return func(*args,**kwargs) return wrapper @deco def index(): '''哈哈哈哈''' print('from index') print(index.__doc__)
练习题:
一:编写函数,(函数执行的时间是随机的)
二:编写装饰器,为函数加上统计时间的功能
三:编写装饰器,为函数加上认证的功能
四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式
五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录
六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
七:为题目五编写装饰器,实现缓存网页内容的功能:
具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中
八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定
注意:时间格式的获取
import time
time.strftime('%Y-%m-%d %X')
*****************************************************************
四 迭代器、生成器
1) 迭代的概念:迭代器都有一个__iter__()方法
#迭代器即迭代的工具,那什么是迭代呢? #迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值 while True: #只是单纯地重复,因而不是迭代 print('===>') l=[1,2,3] count=0 while count < len(l): #迭代 print(l[count]) count+=1
为何要有迭代器?什么是可迭代对象?什么是迭代器对象?
#1、为何要有迭代器? 对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器 #2、什么是可迭代对象? 可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下 'hello'.__iter__ (1,2,3).__iter__ [1,2,3].__iter__ {'a':1}.__iter__ {'a','b'}.__iter__ open('a.txt').__iter__ #3、什么是迭代器对象? 可迭代对象执行obj.__iter__()得到的结果就是迭代器对象 而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象 文件类型是迭代器对象 open('a.txt').__iter__() open('a.txt').__next__() #4、注意: 迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象
迭代器对象的使用
dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
iter_dic.__iter__() is iter_dic #True
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
print(iter_dic.__next__()) #等同于next(iter_dic)
# print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志
#有了迭代器,我们就可以不依赖索引迭代取值了
iter_dic=dic.__iter__()
while 1:
try:
k=next(iter_dic)
print(dic[k])
except StopIteration:
break
#这么写太丑陋了,需要我们自己捕捉异常,控制next,python这么牛逼,能不能帮我解决呢?能,请看for循环
for循环遵循迭代协议。
#基于for循环,我们可以完全不再依赖索引去取值了 dic={'a':1,'b':2,'c':3} for k in dic: print(dic[k]) #for循环的工作原理 #1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic #2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码 #3: 重复过程2,直到捕捉到异常StopIteration,结束循环
fro key val in kwargs.items():
setattr(obj,key,val)给对象增加属性
#把kwargs。items()先转换为键值对,再遍历。
迭代器的优缺点
#优点: - 提供一种统一的、不依赖于索引的迭代方式 - 惰性计算,节省内存 #缺点: - 无法获取长度(只有在next完毕才知道到底有几个值) - 一次性的,只能往后走,不能往前退
2)生成器
什么是生成器
#只要函数内部包含有yield关键字,那么函数名()的结果对象就是生成器,并且不会执行函数内部代码 def func(): print('====>first') yield 1 print('====>second') yield 2 print('====>third') g=func() print(g) #<generator object func at 0x0000000002184360> #g就是生成器
生成器就是迭代器
g.__iter__ g.__next__ #2、所以生成器就是迭代器,因此可以这么取值 res=next(g) print(res)
生成器generator使用之next和send运行流程:
对于普通的生成器,第一个next调用,相当于启动生成器,会从生成器函数的第一行代码开始执行,直到第一次执行完yield语句(第4行)后,跳出生成器函数。 然后第二个next调用,进入生成器函数后,从yield语句的下一句语句(第5行)开始执行,然后重新运行到yield语句,执行后,跳出生成器函数, 后面再次调用next,依次类推。下面是一个列子: 1 def consumer(): 2 r = 'here' 3 for i in xrange(3): 4 yield r 5 r = '200 OK'+ str(i) 6 7 c = consumer() 8 n1 = c.next() 9 n2 = c.next() 10 n3 = c.next() 了解了next()如何让包含yield的函数执行后,我们再来看另外一个非常重要的函数send(msg)。其实next()和send()在一定意义上作用是相似的,
区别是send()可以传递yield表达式的值进去,而next()不能传递特定的值,只能传递None进去。因此,我们可以看做c.next() 和 c.send(None) 作用是一样的。
需要提醒的是,第一次调用时,请使用next()语句或是send(None),不能使用send发送一个非None的值,否则会出错的,因为没有Python yield语句来接收这个值。 下面来着重说明下send执行的顺序。当第一次send(None)(对应11行)时,启动生成器,从生成器函数的第一行代码开始执行,直到第一次执行完yield(对应第4行)后,
跳出生成器函数。这个过程中,n1一直没有定义。 下面运行到send(1)时,进入生成器函数,注意这里与调用next的不同。这里是从第4行开始执行,把1赋值给n1,但是并不执行yield部分。
下面继续从yield的下一语句继续执行,然后重新运行到yield语句,执行后,跳出生成器函数。 即send和next相比,只是开始多了一次赋值的动作,其他运行流程是相同的。 1 def consumer(): 2 r = 'here' 3 while True: 4 n1 = yield r 5 if not n1: 6 return 7 print('[CONSUMER] Consuming %s...' % n1) 8 r = '200 OK'+str(n1) 9 10 def produce(c): 11 aa = c.send(None) 12 n = 0 13 while n < 5: 14 n = n + 1 15 print('[PRODUCER] Producing %s...' % n) 16 r1 = c.send(n) 17 print('[PRODUCER] Consumer return: %s' % r1) 18 c.close() 19 20 c = consumer() 21 produce(c) 运行结果:
[PRODUCER] Producing 1... [CONSUMER] Consuming 1... [PRODUCER] Consumer return: 200 OK1 [PRODUCER] Producing 2... [CONSUMER] Consuming 2... [PRODUCER] Consumer return: 200 OK2 [PRODUCER] Producing 3... [CONSUMER] Consuming 3... [PRODUCER] Consumer return: 200 OK3 [PRODUCER] Producing 4... [CONSUMER] Consuming 4... [PRODUCER] Consumer return: 200 OK4 [PRODUCER] Producing 5... [CONSUMER] Consuming 5... [PRODUCER] Consumer return: 200 OK5
return与yield的区别:
return是函数返回值,当执行到return,后续的代码不再执行;
yield是创建迭代器iteral,称之为生成器generator,让函数生成一个结果序列,而不仅仅是一个值,可以用for来遍历,有点事件触发的意思。
可用现实的一种实物来理解:水车,先yield来装入数据、产出generator object、使用next()来释放;好比水车转动后,车轮上的水槽装入水,随着轮子转动,
被转到下面的水槽就能将水送入水道中流入田里。好处是不用一次计算所以元素,而是用一次算一次,可以节省大量空间
练习
1、自定义函数模拟range(1,7,2)
2、模拟管道,实现功能:tail -f access.log | grep '404'
1 #题目一: 2 def my_range(start,stop,step=1): 3 while start < stop: 4 yield start 5 start+=step 6 7 #执行函数得到生成器,本质就是迭代器 8 obj=my_range(1,7,2) #1 3 5 9 print(next(obj)) 10 print(next(obj)) 11 print(next(obj)) 12 print(next(obj)) #StopIteration 13 14 #应用于for循环 15 for i in my_range(1,7,2): 16 print(i) 17 18 #题目二 19 import time 20 def tail(filepath): 21 with open(filepath,'rb') as f: 22 f.seek(0,2) 23 while True: 24 line=f.readline() 25 if line: 26 yield line 27 else: 28 time.sleep(0.2) 29 30 def grep(pattern,lines): 31 for line in lines: 32 line=line.decode('utf-8') 33 if pattern in line: 34 yield line 35 36 for line in grep('404',tail('access.log')): 37 print(line,end='') 38 39 #测试 40 with open('access.log','a',encoding='utf-8') as f: 41 f.write('出错啦404\n')
协程函数
#yield关键字的另外一种使用形式:表达式形式的yield def eater(name): print('%s 准备开始吃饭啦' %name) food_list=[] while True: food=yield food_list print('%s 吃了 %s' % (name,food)) food_list.append(food) g=eater('egon') g.send(None) #对于表达式形式的yield,在使用时,第一次必须传None,g.send(None)等同于next(g) g.send('蒸羊羔') g.send('蒸鹿茸') g.send('蒸熊掌') g.send('烧素鸭') g.close() g.send('烧素鹅') g.send('烧鹿尾')
练习
3、编写装饰器,实现初始化协程函数的功能
4、实现功能:grep -rl 'python' /etc
#题目一: def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwargs) next(g) return g return wrapper @init def eater(name): print('%s 准备开始吃饭啦' %name) food_list=[] while True: food=yield food_list print('%s 吃了 %s' % (name,food)) food_list.append(food) g=eater('egon') g.send('蒸羊羔') #题目二: #注意:target.send(...)在拿到target的返回值后才算执行结束 import os def init(func): def wrapper(*args,**kwargs): g=func(*args,**kwargs) next(g) return g return wrapper @init def search(target): while True: filepath=yield g=os.walk(filepath) for dirname,_,files in g: for file in files: abs_path=r'%s\%s' %(dirname,file) target.send(abs_path) @init def opener(target): while True: abs_path=yield with open(abs_path,'rb') as f: target.send((f,abs_path)) @init def cat(target): while True: f,abs_path=yield for line in f: res=target.send((line,abs_path)) if res: break @init def grep(pattern,target): tag=False while True: line,abs_path=yield tag tag=False if pattern.encode('utf-8') in line: target.send(abs_path) tag=True @init def printer(): while True: abs_path=yield print(abs_path) g=search(opener(cat(grep('你好',printer())))) # g.send(r'E:\CMS\aaa\db') g=search(opener(cat(grep('python',printer())))) g.send(r'E:\CMS\aaa\db')
五、 三元表达式、列表推导式、生成器表达式、递归、匿名函数、内置函数
https://www.cnblogs.com/wang-sir/articles/9286499.html
六、解压知识:
a,b,c,d='hell' print (a,b,c,d) a,b,c,d = ['h','e','l','l'] print (a,b,c,d) #如果取开头与结尾呢? a,*b,c=[1,2,3,4,5,6,7,8,9] print (a,b,c) 运行结果: Hello, World! h e l l h e l l 1 [2, 3, 4, 5, 6, 7, 8] 9
在字典中的应用:
def func3(dic):
d={}
for k,v in dic.items():#一个键一个值的迭代,使用解压较简单。
if len(v) > 2:
d[k]=v[0:2]
return d
print(func3({'k1':'abcdef','k2':[1,2,3,4],'k3':('a','b','c')}))
a=1
b=2
交换两个值
a,b = b,a
示例代码 https://github.com/triaquae/py3_training/tree/master/atm
简易流程图:https://www.processon.com/view/link/589eb841e4b0999184934329


浙公网安备 33010602011771号