python生成器系列文章目录
第一章 yield — Python (Part I)
文章目录
- python生成器系列文章目录
- 前言
- 1. Generator Function 生成器函数
- 2.并发和并行,抢占式和协作式
- 2.Let’s implement Producer/Consumer pattern using subroutine:
- 生成器的状态 generator’s states
前言
ref:https://medium.com/analytics-vidhya/yield-python-part-i-4dbfe914ad2d
这个老哥把yield讲清楚了,我来学习并且记录一下。
偶尔遇到Yield关键字时,它看起来相当神秘。这里,我们通过查看生成器如何使用yield获取值或将控制权返回给调用者来揭示yield所做的工作。我们也在看生成器generator的不同状态。让我们开始吧。
1. Generator Function 生成器函数
一个函数用了yield表达式后被称为生成器函数。
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被创建在第七行,相应的,生成器generator的执行通过调用next();
我们获得了yield的1个输出因为仅仅调用了一次next,接着generator是在suspend state(暂停/挂起状态),当另一个next()调用的时候,会激活执行并且返回第二个yield的值。像任何迭代器iterator一样,生成器将会exhausted 当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.并发和并行,抢占式和协作式
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中一种产生协程的做法是generator 生成器。
global 表示将变量声明为全局变量
nonlocal 表示将变量声明为外层变量(外层函数的局部变量,而且不能是全局变量)
def average():
count = 0
sum = 0
def inner(value):
nonlocal count
nonlocal sum
count += 1
sum += value
return sum/count
return inner
def 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:
这个图要好好理解一下:
2.Let’s implement Producer/Consumer pattern using subroutine:
from collections import deque
def 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:
yield
def 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`
yield
def coordinator():
dq = deque(maxlen=2)
# instantiate producer and consumer generator
producer = produce_element(dq, 5)
consumer = consume_element(dq)
while True:
try:
# producer fills deque
print('next producer...')
next(producer)
except StopIteration:
break
finally:
# consumer empties deque
print('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
过程解析:
生产2个,消费2个,再生产两个,再消费两个,再生产一个,触发StopIteration,再转向finall 消费1个 整个进程结束。
详细的看英语:
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.
生成器的状态 generator’s states
from inspect import getgeneratorstate
def gen(flowers):
for flower in flowers:
print(f'Inside loop:{getgeneratorstate(flower_gen)}')
yield flower
flower_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_CREATED
getting 1st flower
Inside loop:GEN_RUNNING
--== azalea
After getting first flower: GEN_SUSPENDED
Inside loop:GEN_RUNNING
Inside loop:GEN_RUNNING
Get all flowers: ['Forsythia', 'violas']
After getting all flowers: GEN_CLOSED
Process 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!
下一篇文章介绍如何传值到生成器