Python:生成器
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python:生成器相关的知识,希望对你有一定的参考价值。
生成器算得上是Python语言中最吸引人的特性之一,生成器其实是一种特殊的迭代器,不过这种迭代器更加优雅。它不需要再像上面的类一样写 __iter__() 和 __next__() 方法了,只需要一个 yiled 关键字。
首先迭代提供的嵌套列表中的所有字列表,然后按顺序迭代自列表中的元素。任何包含yield语句的函数称为生成器。除了名字不同意外,它的行为和普通函数也有很大的差别,这就在于它不像return那样返回值,而是每次产生多个值。每次产生一个值(使用yield语句),函数就会被冻结:即函数停在那点等待被激活,函数被激活后就从停止的那点开始执行
nested=[[1,2],[3,4],[5]] def flatten(nested): for sublist in nested: for element in sublist: yield element for num in flatten(nested): print(num) #1 # 2 # 3 # 4 # 5 print(list(flatten(nested)))#[1, 2, 3, 4, 5]
生成器两种创建方式
1.(x*2 for x in range(3))
# a=[x*2 for x in range(1000000)]#不要试,死机 s=(x*2 for x in range(3)) print(s)#<generator object <genexpr> at 0x00000203201E6938> print(next(s)) #等价于print(s.__next__()),in Py2: s.next() print(next(s))print(next(s)) print(next(s))#StopIteration # 生成器就是一个可迭代对象(iterable)
2.yield 生成器对象
生成器是一个包含yield关键字的函数。当它被调用时,在函数体中的代码不会执行,而会返回一个迭代器。每次请求一个值,就会执行生成器中的代码,知道遇到yield或者return语句。
yield语句意味着应该生成一个值,return语句意味着生成器要停止执行(不再生成任何东西,return语句只有在一个生成器中使用时才能进行无参数调用),换句话说生成器又两部分组成:生成器的函数和生成器的迭代器生成器的函数使用def语句定义,包含yield,生成器的迭代器是这个函数返回的部分。
按一种不是很准确的说法,两个实体经常被当做一个,合起来叫做生成器。生成器函数跟普通函数只有一点不一样,就是把 return 换成yield,其中yield是一个语法糖,内部实现了迭代器协议,同时保持状态可以挂起。
def foo(): print("Hello world") yield 1 print("ok") yield 2 foo()#生成器对象,不会执行代码 g=foo() print(g)#<generator object foo at 0x00000230A33569E8> next(g) #Hello world next(g) #ok # next(g)#StopIteration for i in foo():#遍历可迭代对象,对象拥有iter方法 print(i) #Hello world # 1 # ok # 2
用生成器来实现斐波那契数列的例子:一
def fib(max): n, a, b = 0, 0, 1 while n < max: # print(b) yield b a, b = b, a + b n = n + 1 return ‘done‘ f=fib(6) print(f)#<generator object fib at 0x0000025839E56990> #这里,最难理解的就是generator和函数的执行流程不一样。 # 函数是顺序执行,遇到return语句或者最后一行函数语句就返回。 # 而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回, # 再次执行时从上次返回的yield语句处继续执行。 print(f.__next__()) print(f.__next__()) print("________*****______") print(f.__next__()) print(f.__next__()) print(f.__next__())
结果:
1
1
________*****______
2
3
5
二
def fib(max): n, a, b = 0, 0, 1 while n < max: # print(b) yield b a, b = b, a + b n = n + 1 return ‘done‘ f=fib(6) while True: try: x=next(f) print(‘f:‘,x) except StopIteration as e: print("Generator return value:",e.value) break #结果 f: 1 f: 1 f: 2 f: 3 f: 5 f: 8 Generator return value: done
生成器新属性是在开始运行后为生成器提供值的能力。表现为生成器和“外部世界”进行交流的渠道:
- 外部作用域访问生成器的send方法,就像访问next 方法一样,只不过前者使用一个参数(发送的“消息”---任意对象)
- 在内部则挂起生成器,yield现在作为表达式而不是语句使用,换句话说,当生成器重新运行的时候,yield方法返回一个值,也就是外部通过send方法发送的值。如果next 方法被使用,那么yield方法返回None.
- throw方法(使用异常类型调用,还有可选的值以及回溯对象)用于在生成器内引发一个异常(在yield表达式中)
- close 方法(调用时不用参数)用于停止生成器。
def bar():
print(‘ok1‘) count=yield 1 print(count) yield 2 b=bar() next(b) # s=b.send(None)#next(b) 第一次send前如果没有next,只能传一个send(None) # print(s) ret=b.send(‘eee‘) print(ret) # b.send(‘fff‘)
结果:
ok1
eee
2
send工作方法
def f(): print("ok") s=yield 7 print(s) yield 8 f=f() print(f.send(None)) # ok # 7 print(next(f)) # None # 8 #print(f.send(None))等同于print(next(f)),执行流程:打印ok,yield7,当再next进来时:将None赋值给s,然后返回8,可以通过断点来观察
吃包子案例
import time def consumer(name): print("%s 准备吃包子啦!" %name) while True: baozi = yield print("包子[%s]来了,被[%s]吃了!" %(baozi,name)) def producer(name): c = consumer(‘A‘)#生成器对象 c2 = consumer(‘B‘)#生成器对象 c.__next__()#执行生成器 c2.__next__() print("%s开始准备做包子啦!"%name) for i in range(1,6,2): time.sleep(1) print("做了2个包子!") c.send(i) c2.send(i+1) producer("greg")
协程应用:
所谓协同程序也就是是可以挂起,恢复,有多个进入点。其实说白了,也就是说多个函数可以同时进行,可以相互之间发送消息等。
import queue def tt(): for x in range(3): print (‘tt‘+str(x) ) yield def gg(): for x in range(3): print (‘xx‘+str(x) ) yield class Task(): def __init__(self): self._queue = queue.Queue() def add(self,gen): self._queue.put(gen) def run(self): while not self._queue.empty(): for i in range(self._queue.qsize()): try: gen= self._queue.get() gen.send(None) except StopIteration: pass else: self._queue.put(gen) t=Task() t.add(tt()) t.add(gg()) t.run() # tt0 # xx0 # tt1 # xx1 # tt2 # xx2
以上是关于Python:生成器的主要内容,如果未能解决你的问题,请参考以下文章