1. 程式人生 > >Python:生成器

Python:生成器

創建 進入 表達式 凍結 round 參數 () ffffff 保持

生成器算得上是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:生成器