南昌net網(wǎng)站開發(fā)深圳門戶網(wǎng)站
python生成器系列文章目錄
第一章 yield — Python (Part I)
文章目錄
- python生成器系列文章目錄
- 前言
- 1. Generator Function 生成器函數(shù)
- 2.并發(fā)和并行,搶占式和協(xié)作式
- 2.Let’s implement Producer/Consumer pattern using subroutine:
- 生成器的狀態(tài) generator’s states
前言
ref:https://medium.com/analytics-vidhya/yield-python-part-i-4dbfe914ad2d
這個(gè)老哥把yield講清楚了,我來(lái)學(xué)習(xí)并且記錄一下。
偶爾遇到Y(jié)ield關(guān)鍵字時(shí),它看起來(lái)相當(dāng)神秘。這里,我們通過(guò)查看生成器如何使用yield獲取值或?qū)⒖刂茩?quán)返回給調(diào)用者來(lái)揭示yield所做的工作。我們也在看生成器generator的不同狀態(tài)。讓我們開始吧。
1. Generator Function 生成器函數(shù)
一個(gè)函數(shù)用了yield表達(dá)式后被稱為生成器函數(shù)。
def happy_birthday_song(name='Eric'):yield "Happy Birthday to you"yield "Happy Birthday to you"yield f"Happy Birthday dear {name}"yield "Happy Birthday to you"
birthday_song_gen = happy_birthday_song() # generator creation
print(next(birthday_song_gen)) # prints first yield's value
birthday_song_gen 作為Generator被創(chuàng)建在第七行,相應(yīng)的,生成器generator的執(zhí)行通過(guò)調(diào)用next();
我們獲得了yield的1個(gè)輸出因?yàn)閮H僅調(diào)用了一次next,接著generator是在suspend state(暫停/掛起狀態(tài)),當(dāng)另一個(gè)next()調(diào)用的時(shí)候,會(huì)激活執(zhí)行并且返回第二個(gè)yield的值。像任何迭代器iterator一樣,生成器將會(huì)exhausted 當(dāng)stopIteration is encountered.
def happy_birthday_song(name='Eric'):yield "Happy Birthday to you"yield "Happy Birthday to you"yield f"Happy Birthday dear {name}"yield "Happy Birthday to you"birthday_song_gen = happy_birthday_song() # generator creation
print(next(birthday_song_gen)) # prints first yield's value# print rest of the yield's value
try:while True:print(next(birthday_song_gen))
except StopIteration:print('exhausted...')
2.并發(fā)和并行,搶占式和協(xié)作式
Cooperative multitasking is completely controlled by developer. Coroutine (Cooperative routine) is an example of cooperative multitasking.
Preemptive multitasking is not controlled by developer and have some sort of scheduler involved.
One of the ways to create coroutine in Python is generator.
在python中一種產(chǎn)生協(xié)程的做法是generator 生成器。
global 表示將變量聲明為全局變量
nonlocal 表示將變量聲明為外層變量(外層函數(shù)的局部變量,而且不能是全局變量)
def average():count = 0sum = 0def inner(value):nonlocal countnonlocal sumcount += 1sum += valuereturn sum/countreturn innerdef running_average(iterable):avg = average()for value in iterable:running_average = avg(value):print(running_average)
iterable = [1,2,3,4,5]
running_average(iterable)
輸出:
The program control flow looks like this:
這個(gè)圖要好好理解一下:
2.Let’s implement Producer/Consumer pattern using subroutine:
from collections import dequedef produce_element(dq, n):print('\nIn producer ...\n')for i in range(n):dq.appendleft(i)print(f'appended {i}')# if deque is full, return the control back to `coordinator`if len(dq) == dq.maxlen:yielddef consume_element(dq):print('\nIn consumer...\n')while True:while len(dq) > 0:item = dq.pop()print(f'popped {item}')# once deque is empty, return the control back to `coordinator`yielddef coordinator():dq = deque(maxlen=2)# instantiate producer and consumer generatorproducer = produce_element(dq, 5)consumer = consume_element(dq)while True:try:# producer fills dequeprint('next producer...')next(producer)except StopIteration:breakfinally:# consumer empties dequeprint('next consumer...')next(consumer)if __name__ == '__main__':coordinator()
output looks like this:
C:\Users\HP\.conda\envs\torch1.8\python.exe "C:\Program Files\JetBrains\PyCharm 2021.1.3\plugins\python\helpers\pydev\pydevd.py" --multiproc --qt-support=auto --client 127.0.0.1 --port 59586 --file D:/code/python_project/01-coroutine-py-mooc/8/demo_ccc.py
Connected to pydev debugger (build 211.7628.24)
next producer...In producer..next consumer ...In consumer... popped 0
popped 1
next producer...
next consumer ...
popped 2
popped 3
next producer...
next consumer ...Process finished with exit code -1
過(guò)程解析:
生產(chǎn)2個(gè),消費(fèi)2個(gè),再生產(chǎn)兩個(gè),再消費(fèi)兩個(gè),再生產(chǎn)一個(gè),觸發(fā)StopIteration,再轉(zhuǎn)向finall 消費(fèi)1個(gè) 整個(gè)進(jìn)程結(jié)束。
詳細(xì)的看英語(yǔ):
What’s happening? Well, the following thing is happening:
-
create a limited size deque , here size of 2
-
coordinator creates an instance of producer generator and also mentioning how many elements it want to generate
-
coordinator creates an instance of consumer generator
-
producer runs until deque is filled and yields control back to caller
-
consumer runs until deque is empty and yields control back to caller
Steps 3 and 4 are repeated until all elements the producer wanted to produce is complete. This coordination of consumer and producer is possible due to we being able to control state of a control flow.
生成器的狀態(tài) generator’s states
from inspect import getgeneratorstatedef gen(flowers):for flower in flowers:print(f'Inside loop:{getgeneratorstate(flower_gen)}')yield flowerflower_gen = gen(['azalea', 'Forsythia', 'violas'])print(f"After generator creation:{getgeneratorstate(flower_gen)}\n")print('getting 1st flower')print("--==", next(flower_gen))print(f'After getting first flower: {getgeneratorstate(flower_gen)}\n')print(f'Get all flowers: {list(flower_gen)}\n')print(f'After getting all flowers: {getgeneratorstate(flower_gen)}')
輸出:
C:\Users\HP\.conda\envs\torch1.8\python.exe D:/code/python_project/01-coroutine-py-mooc/8/demo_ccc.py
After generator creation:GEN_CREATEDgetting 1st flower
Inside loop:GEN_RUNNING
--== azalea
After getting first flower: GEN_SUSPENDEDInside loop:GEN_RUNNING
Inside loop:GEN_RUNNING
Get all flowers: ['Forsythia', 'violas']After getting all flowers: GEN_CLOSEDProcess finished with exit code 0
We have a handy getgeneratorstate method from inspect module that gives state of a generator. From the output, we see there are four different states:
- GEN_CREATED
- GEN_RUNNING
- GEN_SUSPENDED
- GEN_CLOSED
GEN_CREATED is a state when we instantiate a generator. GEN_RUNNING is a state when a generator is yielding value. GEN_SUSPENDED is a state when a generator has yielded value. GEN_CLOSED is a state when a generator is exhausted.
In summary, yield is used by generators to produce value or give control back to caller and generator has 4 states.
My next article will be sending values to generators!
下一篇文章介紹如何傳值到生成器