17. 生成器
Posted 村里唯一的运维
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了17. 生成器相关的知识,希望对你有一定的参考价值。
生成器
1. 概述
元组推导式是生成器(generator)
生成器本质是迭代器,允许自定义逻辑的迭代器
迭代器和生成器区别:
迭代器本身是系统内置的.重写不了.而生成器是用户自定义的,可以重写迭代逻辑
生成器可以用两种方式创建:
(1)生成器表达式 (里面是推导式,外面用圆括号)
(2)生成器函数 (用def定义,里面含有yield)
2. 生成器的基本用法
生成器表达式
gen = ( i for i in range(5))
print(gen,type(gen))
生成器中获取数据的方式
for循环
gen = ( i for i in range(5))
for i in gen:
print(i)
next调用
gen = ( i for i in range(5))
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
for循环和next配合调用生成器中的数据
gen = ( i for i in range(5))
for i in range(5):
res = next(gen)
print(res)
3. 生成器函数
yield 类似于 return
共同点在于:执行到这句话都会把值返回出去
不同点在于:yield每次返回时,会记住上次离开时执行的位置 , 下次在调用生成器 , 会从上次执行的位置往下走
而return直接终止函数,每次重头调用.
生成器函数基本用法
def mygen():
print("one")
yield 1
print("two")
yield 2
print("three")
yield 3
初始化生成器函数 返回生成器对象, 简称生成器
gen = mygen()
print(isinstance(gen,Iterator))
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
# res = next(gen) error 越界错误
# print(res)
上述代码解析
代码解析:
通过next调用生成器对象,找到mygen函数,代码依次从上到下执行:
先走12行 print("one") yield 1 记录当前执行代码的位置状态 , 添加阻塞 ,把1直接返回,等待下一次调用
在通过next进行调用时,
从上一次记录的那个位置状态往下走,
print("two") yield 2 记录当前执行代码的位置状态 , 添加阻塞 ,把2直接返回,等待下一次调用
在通过next进行调用时,
从上一次记录的那个位置状态往下走,
print("three") yield 3 记录当前执行代码的位置状态 , 添加阻塞 ,把3直接返回,等待下一次调用
在通过next进行调用时,
从上一次记录的那个位置状态往下走,发现已经没有yield,直接越界报错.
send
不但可以获取值,还能发送值,发送给上一个yield
next和send区别:
next 只能取值
send 不但能取值,还能发送值
send注意点:
第一个 send 不能给 yield 传值 默认只能写None
最后一个yield 接收不到send的发送值
示例说明
def mygen():
print("program start")
res = yield 1
print(res)
res = yield 2
print(res)
res = yield 3
print(res)
print("program end")
# 初始化生成器函数 => 变成生成器对象 简称 生成器
print("<===================>")
gen = mygen()
res2 = gen.send(None)
print(res2)
res2 = gen.send(1111)
print(res2)
res2 = gen.send(2222)
print(res2)
代码解析
代码解析:
res = gen.send(None) 第一次通过send 调用时,因为send 默认发送给上一个yield,所以第一次发送不了,只能默认发送None,属于硬性语法
第一次代码调用时,只走到了3行 ,记录代码执行的位置状态,添加阻塞,返回值1
在调用时,通过send(1111)给yield 1 res 接收到了send发送过来的返回值 , 生成器函数中从上一次代码记录的位置,往下走,走4行代码,打印 1111
继续执行5行代码, 把yield 2返回,记录代码执行的位置状态,添加阻塞,返回值2
在调用时,通过send(2222)给yield 2 res 接收到了send发送过来的返回值 , 生成器函数中从上一次代码记录的位置,往下走,走6行代码,打印 2222
继续执行7行代码, 把yield 3返回,记录代码执行的位置状态,添加阻塞,返回值3
在调用时,通过send(3333)给yield 3 res 接收到了send发送过来的返回值 , 生成器函数中从上一次代码记录的位置,往下走,走85行代码,打印 3333
问题是再也没有yield 响应的返回了,直接报错.
yield from : 将一个可迭代对象变成一个迭代器返回
系统默认会把from后面的可迭代性数据,每个元素扔到迭代器中,通过next一个一个调用
def mygen():
yield from ["张三","李四","王五","大刀"]
gen = mygen()
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)
Python-生成器
1、生成器***
之前在列表解析中已经介绍了 生成器表达式。这里介绍生成器函数。
生成器:generator
生成器指的是生成器对象,可以有生成器表达式得到,可以使用yield 关键字得到一个生成器函数,调用这个函数就得到一个生成器对象。
生成器函数:
函数体重包含yield语句 的函数,返回生成器对象
生成器对象,是一个可迭代对象,是一个迭代器。
生成器对象,是惰性求值的
举例 1:
1 def fn(): 2 for i in range(5): 3 yield i 4 5 print(type(fn())) # <class ‘generator‘> 6 7 for x in fn(): 8 print(x) 9 print(‘-------------------------‘) 10 for x in fn():# 这个第二次还能继续循环迭代是因为第二次循环又重新调用了fn(),作为一个生成器对象 11 print(x) 12 print(‘-------------------------‘) 13 14 g = fn() # 15 16 for x in g: 17 print(x) 18 print(‘-------------------------‘) 19 for x in g: # 还是从之前的生成器中继续迭代 20 print(x)
举例2:
1 def fen(): 2 print(‘line 1‘) 3 yield 1 4 print(‘line 2‘) 5 yield 2 6 print(‘line 3‘) 7 for i in range(5): 8 print(i) 9 return 3 10 g = fen() 11 12 count = 0 13 for x in g: 14 count += 1 15 print(x,count) 16 17 # line 1 18 # 1 1 19 # line 2 20 # 2 2 21 # line 3 22 # 0 23 # 1 24 # 2 25 # 3 26 # 4
总结:
-
-
- 生成器函数中,使用过个yield 与,执行一次后会暂停执行,把yield表达式的值返回
- 再次执行会执行到下一个yield语句
- return 语句 依然可以终止函数运行,但return 语句的返回值不能被获取到
- return 会导致无法继续去哦去下一个值,抛出StopIteration 异常
- 如果 函数没有显示的return 语句,如果生成器函数执行到结尾一样会抛出StopIteartion异常
-
生成器函数总结:
- 包含yield 语句的生成器函数生成 生成器对象的时候,生成器函数的函数体不会立即执行
- next(generator) 会从函数的当前位置向后执行到之后碰到拿到的第一个yield语句,会弹出,并暂停函数执行。
- 再次调用next函数,和上一条处理过程一样
- 没有多余的yield 语句能被执行,继续调用next函数,会抛出StopIteration
举例 3:
1 def counter(): 2 i = 0 3 while True: 4 i += 1 5 yield i 6 def inc(c): 7 return next(c) 8 9 c = counter() #每次都要外部传参 10 print(inc(c)) # 1 11 print(inc(c)) # 2 12 13 def counter(): 14 i = 0 15 while True: 16 i += 1 17 yield i 18 def inc(): 19 c = counter() # 每次都重新调用,所以每次都拿到同样的值 20 return next(c) 21 22 print(inc()) # 1 23 print(inc()) # 1 24 print(inc()) # 1
举例 4:
1 ‘‘‘ 2 上面的优化: 3 1、不需要外部传参,感觉 inc() 像一个生成器对象 4 2、内层函数作为一个生成器函数,并且在同级进行调用,作为生成器对象 5 3、外层函数将生成器对象地址返回给 外层函数 6 4、全局调用外层函数,就相当于是一个生成器对象。 7 ‘‘‘ 8 def inc(): 9 def countes(): 10 i = 0 11 while True: 12 i += 1 13 yield i 14 c = countes() 15 return lambda :next(c) 16 17 foo = inc() 18 print((foo())) # 1 19 print((foo())) # 2
举例 5:
1 def fib(n): 2 a = 0 3 b = 1 4 for i in range(n): 5 a, b = b, a + b 6 yield a 7 8 foo = fib(5) 9 # for _ in range(5): 10 # print(next(foo)) 11 12 for i in foo: 13 print(i) 14 15 16 17 def fib(): 18 x = 0 19 y = 1 20 while True: 21 yield y 22 x, y = y, x + y 23 24 foo = fib() 25 for _ in range(5): 26 print(next(foo)) 27 28 print(‘-------‘) 29 30 for _ in range(10): 31 # next(foo) 32 print(next(foo),‘--‘) 33 34 # 1 35 # 1 36 # 2 37 # 3 38 # 5 39 # ------- 40 # 8 -- 41 # 13 -- 42 # 21 -- 43 # 34 -- 44 # 55 -- 45 # 89 -- 46 # 144 -- 47 # 233 -- 48 # 377 -- 49 # 610 --
生成器应用:yield引出的(遇到yield,会让出控制权)
-
- 协程Co-routine
- 生成器的高级用法
- 比进程、线程轻量级
- 是在用户空间调度函数的一种实现
- Python3 asyncIO 就是协程实现,已经加入标准库
- Python3.5 使用async,await关键字直接原生支持协程
- 协程调度器实现思路:
- 有两个生成器A,B
- next(A) 后,A 执行到了yield 语句暂停,然后去执行next(B),B执行到yield 语句也暂停,然后再次调用next(A),再调用next(B),周而复始,就实现了调度的效果,
-
1 # 在用户空间就做了调度 2 def a(): 3 for i in range(5): 4 yield i 5 6 def b(): 7 for i in range(5): 8 yield i 9 10 a = a() 11 b = b() 12 13 for i in range(10): 14 if i % 2 == 0: 15 print(next(a), ‘a‘) 16 else: 17 print(next(b), ‘b‘) 18 # 0 a 19 # 0 b 20 # 1 a 21 # 1 b 22 # 2 a 23 # 2 b 24 # 3 a 25 # 3 b 26 # 4 a 27 # 4 b
- 可以引入调度的策略来实现切换的方式
- 有两个生成器A,B
- 协程是一种非抢占式调度
- 协程效率是比较高的
- 协程Co-routine
yield from:
1 def inc(): 2 for x in range(100): 3 yield x 4 5 || 6 || 7 8 def inc(): 9 yield from range(100)
yield from 是Python3.3 出现的新的语法
yield from iteable 是 for item in iterable : yield item 形式的语法糖
从 可迭代 对象中一个个拿 元素
1 def counter(n): 2 for x in range(n): 3 yield x 4 5 def inc(n): 6 yield from counter(n) 7 8 foo = inc(10) 9 10 print(next(foo)) # 0 11 print(next(foo)) # 1
以上是关于17. 生成器的主要内容,如果未能解决你的问题,请参考以下文章