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:生成器的主要内容,如果未能解决你的问题,请参考以下文章

postman 自动生成 curl 代码片段

Apollo Codegen 没有找到生成代码的操作或片段

前端开发工具vscode如何快速生成代码片段

前端开发工具vscode如何快速生成代码片段

vscode代码片段生成vue模板

常用python日期日志获取内容循环的代码片段