函数体中出现yield的关键字,这个函数调用就是一个生成器
def gen():
print("generator start")
x = yield 1
print("received x =",x)
print(type(gen())) #
一个生成器对象有多种状态,可以通过客户端代码与生成器对象进行交互
客户端代码与生成器对象交互的三个主要方法:
下面的示例大家可以跑一下,如果能够理解这个过程,那么应该对生成器和yield有些理解
import inspect
from functools import wraps
def prime_generator(func):
"""
预激活生成器的装饰器
:return:
"""
@wraps(func)
def wrapper(*args, **kwargs):
gen = func(*args, **kwargs)
next(gen)
return gen
return wrapper
@prime_generator
def average():
"""
生成器函数
:return:
"""
count = 0
total = 0
avg = 0
while True:
try:
x = yield count, total, avg
except ValueError:
print("输入的值不符合要求")
continue
if x == None:
break
count += 1
total += x
avg = total / count
def client():
"""
客户端代码
:return:
"""
avg = average() # 创建生成器对象
print(inspect.getgeneratorstate(avg)) # GEN_CREATED
# s1 = next(avg) # 预激活生成器,生成器能运行,运行到第一个yield进入等待状态 , avg.send(None) 等同于 next(avg)
# print(s1)
while True:
x = input("请输入值:")
if x == "q":
break
try:
x = float(x)
res = avg.send(x)
print(f"count:{
res[0]},total:{
res[1]},avg:{
res[2]}")
except ValueError:
avg.throw(ValueError)
print(inspect.getgeneratorstate(avg)) # GEN_SUSPENDED
avg.close()
print(inspect.getgeneratorstate(avg)) # GEN_CLOSED
if __name__ == '__main__':
client()
引入yield from
def gen():
for i in "AB":
yield i
for i in range(1,3):
yield i
g = gen()
for i in g:
print(i)
"""
A
B
1
2
"""
下面代码的输出结果与上面相同
def gen():
yield from subgen()
yield from subgen2()
def subgen():
for i in "AB":
yield i
def subgen2():
for i in range(1, 3):
yield i
g = gen()
for i in g:
print(i)
"""
A
B
1
2
"""
首先,yield和yield from关键字必须写在函数体内。
含有yield from关键字的生成器称为委派生成器,yield from会把执行权交给其后面的生成器参数,当子生成器抛出StopIteration,执行权回到委派生成器内部。
from collections import namedtuple
Result = namedtuple('Result', ['count', 'average'])
def averager():
total = 0.0
count = 0
average = 0
while True:
temp = yield
if temp is None:
break
total += temp
count += 1
average = total / count
return Result(count, average)
def grouper(results, key):
while True:
results[key] = yield from averager()
def report(results):
for key, result in sorted(results.items()):
group, unit = key.split(';')
print('{:2} {:5} averaging {:.2f}{}'.format(result.count, group, result.average, unit))
def main(data):
results = {
}
group = None
for k, v in data.items():
group = grouper(results, k)
next(group)
for v_ in v:
group.send(v_)
group.send(None)
report(results)
if group: # 停止委派器生成器
group.close()
data = {
'girls;kg': [40, 41, 42, 43, 44, 54],
'girls;m': [1.5, 1.6, 1.8, 1.5, 1.45, 1.6],
'boys;kg': [50, 51, 62, 53, 54, 54],
'boys;m': [1.6, 1.8, 1.8, 1.7, 1.55, 1.6],
}
if __name__ == '__main__':
main(data)
通过一张图,来剖析一下这段代码的执行流程:
我的文字表达能力可能有限,解释的可能不清晰,如果读者有条件,可以把代码Copy到Pycharm中,打上断点,debug一下,看一下代码的执行流程,这样能够帮助大家更好的理解。
def consumer():
while True:
x = yield
print(f"消费者消费了{
x}")
def productor(c):
next(c)
while True:
x = random.randint(0, 100)
print(f"生产者生产了{
x}")
c.send(x)
time.sleep(1)
if __name__ == '__main__':
c = consumer()
productor(c)
yield和yield 是python异步程序的基础,所以这是很重要的概念,这些概念相比同步还是比较抽象,所以不是很好理解,但是很重要。