引入
生成器generator:生成器指的是生成器对象,可以由生成器表达式得到,也可以使用yield关键字得到一个生成器函数,调用这个函数得到一个生成器对象
生成器函数:
- 函数体中包含yeild语句的函数,返回生成器对象
- 生成器对象,是一个可迭代对象,是一个迭代器
- 生成器对象,是延迟求值,惰性求值的
yield与return的比较
- 遇到yield,函数就会让出此次操作,去执行函数体别的语句
- return,直接打断函数的执行,返回结果
普通函数,生成器函数的比较
例子一: 单个yield,使用
def inc(): for i in range(2): yield i print(type(inc)) print(type(inc())) x = inc() print(type(x)) print(next(x)) #数值通过next()来调用 print(next(x)) print(next(x,"END")) #取到最后没有元素,给个缺省值 结果: <class 'function'> <class 'generator'> <class 'generator'> 0 1 END
- 普通函数调用使用fn(),函数会立即执行完毕,但是生成器函数可以使用next(),函数来多次执行
例子二:函数中使用多个yield
def gen(): print('line 1') yield 1 print('line 2') yield 2 print('line 3') return 3 next(gen()) # line 1 next(gen()) # line 1 重新调用了函数,所以会打印两次line 1 g = gen() print(next(g)) # 答应line 1 print(next(g)) # 打印line 2 print(next(g)) # 报错StopIteration 结果: line 1 line 1 line 1 1 line 2 2 line 3 StopIteration: 3
- 生成器函数中,使用多个yield语句,执行一次后,会暂停使用,把yield表达式的值返回
- 再次执行会执行到下一个yield语句
- return语句依然可以终止函数运行,单return语句的返回值不能不能被获取到
- return会导致无法继续获取下一个值,抛出Stoplteration异常
- 如果函数没有显示的return语句,如果生成器函数执行到结尾,一样会抛出stopleteration
生成器函数总结:
生成器函数:
- 包含yield语句的生成器函数生成,生成器对象时候,生成器函数的函数的函数体不会立即执行
- next(generator)会从函数的当前位置向后执行到之后碰到的第一个yield语句,会弹出值,并暂定函数执行
- 再次调用next函数,和上一条一样的处理过程
- 没有多余的yield语句能被执行,继续调用next函数,会抛出Stopleteration
生成器应用
应用于无限循环
def counter(): i = 0 while True: i += 1 yield i def inc(c): return next(c) c = counter() print(inc(c)) print(inc(c)) print(inc(c)) 结果 1 2 3
- counter() 函数负责生成数值,inc() 负责取值
计数器的应用
def inc(): def counter(): i = 0 while True: i += 1 yield i c = counter() return lambda : next(c) foo = inc() print(foo()) print(foo()) print(foo()) 结果: 1 2 3
- lambda表达式时匿名函数
- return返回的时一个匿名函数
处理递归问题
def fib(): x = 0 y = 1 while True: yield y x, y = y, x+y foo = fib() for _ in range(2): # 循环取值 print(next(foo)) for _ in range(2): # 循环取值 next(foo) print(next(foo)) # 取第n个值 结果: 1 1 5
携程coroutine
- 生成器的高级用法
- 比进程、线程轻量级
- 是在用户调度函数的一种实现
- Python3 asyncio 就是协程实现,已经加入到标准库
- Python3.5 使用async、await 关键字直接原生支持协程
- 协程调度器实现思路
- 有两个生成器A、B
- next(A)后,A执行到了yield语句暂停,然后去执行next(B) ,B执行到yield语句也暂停,然后在次调用next(A),再调用next(B),周而复始,就实现了调度的效果
- 可以引入调度的策略来实现切换的方式
- 协程时一种非抢占式调度
yield from
yield from 时python 3.3 出现的新的语法
yield from iterable 是 for item in interable : yield item 形式的语法
- 从可迭代对象中一个个的拿元素
def counter(n): # ࣿm֘ۨଙї֘ for x in range(n): yield x def inc(n): yield from counter(n) foo = inc(10) print(next(foo)) print(next(foo)) 结果: 0 1
举例:yield from
def inc(): def inc(): for x in range(1000): #等价于 yield from range(100) yield x