Python-匿名函数、生成器函数、协程原理、yield from语法
1、匿名函数
1.1、什么是匿名函数
匿名:隐藏名字,即没有名称
匿名函数:没有名字的函数。
函数没有名字该如何定义?函数没有名字如何调用?
1.2、Lambda表达式
1.2.1、语法
使用lambda关键字定义匿名函数,格式为 lambda [参数列表]: 表达式 参数列表不需要小括号。无参就不写参数 冒号用来分割参数列表和表达式部分 不需要使用return。表达式的值,就是匿名函数的返回值。表达式中不能出现等号 lambda表达式(匿名函数)只能写在一行上,也称为单行函数
1.2.2、示例
lambda x: x ** 2 # 定义 (lambda x: x ** 2)(4) # 调用 foo = lambda x,y: (x+y) ** 2 # 定义函数 foo(1, 2) # 等价于 def foo(x,y): return (x+y) ** 2
1.3、使用lambda表达式简化代码示例
1.3.1、返回常量的函数
print((lambda :0)()) print((lambda x:100)(1))
1.3.2、加法匿名函数,带缺省值
print((lambda x, y=3: x + y)(5)) print((lambda x, y=3: x + y)(5, 6))
1.3.3、keyword-only参数
print((lambda x, *, y=30: x + y)(5)) print((lambda x, *, y=30: x + y)(5, y=10))
1.3.4、可变参数
print((lambda *args: (x for x in args))(*range(5))) print((lambda *args: [x+1 for x in args])(*range(5))) print((lambda *args: {x%2 for x in args})(*range(5)))
1.4、应用案例
1.4.1、构建一个字典,所有key对应的值是一个列表,创建新的kv对的值也是空列表
d={c:[] for c in 'abcde'} d['a'].append(10) d['f']=[] d['f'].append(20) print(d)
1.4.2、defaultdict
from collections import defaultdict d = defaultdict(list) # lambda : list() d['a'].append('10') # d['a'] = list() d['f'].append('20') print(d)
1.4.3、sorted
x = ['a', 1, 'b', 20, 'c', 32] print(sorted(x, key=str)) # 如果按照数字排序怎么做? x = ['a', 1, 'b', 20, 'c', 32] print(sorted(x, key=lambda x: x if isinstance(x, int) else int(x, 16)))
2、生成器函数
2.1、创建生成器方式
1、生成器表达式 2、生成器函数 函数体代码中包含yield语句的函数 与普通函数调用不同,生成器函数调用返回的是生成器对象
2.2、生成器表达式示例
m = (i for i in range(5)) print(type(m)) print(next(m)) print(next(m))
2.3、生成器函数示例
def inc(): for i in range(5): yield i print(type(inc)) print(type(inc())) # 生成器函数一定要调用,返回生成器对象 g = inc() # 返回新的生成器对象 print(next(g)) for x in g: print(x) print('-------------------') for x in g: # 还能迭代出元素吗? print(x)
2.4、生成器执行流程
2.4.1、流程说明
普通函数调用,函数会立即执行直到执行完毕。
生成器函数调用,并不会立即执行函数体,而是返回一个生成器对象,需要使用next函数来驱动这个生成器对象,或者使用循环来驱动。
生成器表达式和生成器函数都可以得到生成器对象,只不过生成器函数可以写更加复杂的逻辑。
2.4.2、代码
def gen(): print(1) yield 2 print(3) yield 4 print(5) return 6 yield 7 #print(next(gen())) # 1 2 # print(next(gen())) # 1 2 g = gen() print(next(g)) print(next(g)) # print(next(g)) # return的值可以拿到吗? 报错StopIteration: 6 print(next(g, 'End')) # 没有元素不想抛异常,给个缺省值
2.4.3、代码分析
在生成器函数中,可以多次yield,每执行一次yield后会暂停执行,把yield表达式的值返回再次执行会执行到下一个yield语句又会暂停执行函数返回
return语句依然可以终止函数运行,但return语句的返回值不能被获取到
return会导致当前函数返回,无法继续执行,也无法继续获取下一个值,抛出StopIteration异常
如果函数没有显式的return语句,如果生成器函数执行到结尾(相当于执行了return None),一样会抛出StopIteration异常
2.5、生成器函数
包含yield语句的生成器函数调用后,生成生成器对象的时候,生成器函数的函数体不会立即执行
next(generator) 会从函数的当前位置向后执行到之后碰到的第一个yield语句,会弹出值,并暂停函数执行
再次调用next函数,和上一条一样的处理过程
继续调用next函数,生成器函数如果结束执行了(显式或隐式调用了return语句),会抛出StopIteration异常
2.6、应用案例
2.6.1、无限循环
def counter(): i = 0 while True: i += 1 yield i c = counter() print(next(c)) # 1 print(next(c)) # 2 print(next(c)) # 3
2.6.2、计数器-非正常
def inc(): def counter(): i = 0 while True: i += 1 yield i c = counter() return next(c) print(inc()) # 1 print(inc()) # 1 print(inc()) # 1
2.6.3、计数器-正常
def inc(): def counter(): i = 0 while True: i += 1 yield i c = counter() def inner(): return next(c) return inner # return lambda : next(c) foo = inc() print(foo()) # 1 print(foo()) # 2 print(foo()) # 3
2.6.4、斐波那契数列
def fib(): a=0 b=1 while True: yield b a,b=b,a+b f = fib() for i in range(1,102): print(i,next(f))
4、协程Coroutine
生成器的高级用法 它比进程、线程轻量级,是在用户空间调度函数的一种实现 Python3 asyncio就是协程实现,已经加入到标准库 Python3.5 使用async、await关键字直接原生支持协程 协程调度器实现思路 有2个生成器A、B next(A)后,A执行到了yield语句暂停,然后去执行next(B),B执行到yield语句也暂停,然后再次调 用next(A),再调用next(B)在,周而复始,就实现了调度的效果 可以引入调度的策略来实现切换的方式 协程是一种非抢占式调度
5、yield from语法
5.1、简介
从Python 3.3开始增加了yield from语法,
使得 yield from iterable 等价于 for item in iterable: yield item 。
5.2、作用
yield from就是一种简化语法的语法糖。
5.3、示例
# 方式一 def inc(): for x in range(1000): yield x # 方式二:简化 def inc_m(): yield from range(1000)# 注意这个函数出现了yield,也是生成器函数 foo = inc_m() print(next(foo)) print(next(foo)) print(next(foo))
5.4、小结
本质上yield from的意思就是,从from后面的可迭代对象中拿元素一个个yield出去。