- yield from 的嵌套用法
# 字符串
astr='ABC'
# 列表
alist=[1,2,3]
# 字典
adict={"name":"wangbm","age":18}
# 生成器
agen=(i for i in range(4,8))
def gen(*args, **kw):
for item in args:
yield from item
new_list=gen(astr, alist, adict, agen)
print(list(new_list))
# ['A', 'B', 'C', 1, 2, 3, 'name', 'age', 4, 5, 6, 7]
这是 yield from 的基本用法,代替了for循环,可以简化嵌套,for与while True 不同的一点是会处理 StopIteration报错, StopIteration是协程运行到末端进行的报错。
- 用yield from 进行协程部件交互
from collections import namedtuple
Result = namedtuple('Result', 'total count average')
def averager():
total = 0.0
count = 0
average = 0.0
while True:
term = yield average
if term == None:
break
# print('total is {},count is {}, average is {}'.format(total, count, average))
total += term
count += 1
average = total/count
return Result(total, count, average)
def grouper(results, key):
while True:
results[key] = yield from averager()
# print(results[key])
# print(results[key].total)
def Client(data):
results = {}
for key, values in data.items():
group = grouper(results, key)
next(group)
for value in values:
group.send(value)
group.send(None)
# print(results)
report(results)
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))
data = {'girls;kg' : [40.9, 38, 44.3 , 68.6],
'girls;m' : [1.6, 1.7, 1.8, 1.59],
'boys;kg' : [39.0, 47.0, 55.4, 48.8],
'boys;m' : [1.7, 1.8, 1.75, 1.85]}
if __name__ == '__main__':
Client(data)
运行结果:
4 boys averaging 47.55kg
4 boys averaging 1.77m
4 girls averaging 47.95kg
4 girls averaging 1.67m
Process finished with exit code 0
grouper作为委派生成器,其中的yield from仅仅起到传递作用,并不作为 yield一样的生成器生成数据。averager作为子生成器 接収来自 grouper 发送send来的数据,进行计算,要注意到 这里return的数据是在 生成器closed以后才会返回, 所以要指定while循环的退出条件, 并且在测试中也必须要进行终止操作。最后Client 客户端通过委派生成器进行调用子生成器。
这里的委派生成器存在的意义就是简化编程,yield from 与for循环一样, 会自己处理退出异常,不需要自己实现。
- 协程的意义
看下面一个出租车仿真编程
import random
import collections
import queue
import argparse
import time
#
DEFAULT_NUMBER_OF_TAXTIS = 3
DEFAULT_END_TIME = 180
SEARCH_DURATION = 5
TRIP_DURATION = 20
DEPARTURE_INTERVAL = 5
Event = collections.namedtuple('Event', 'time proc action')
#begin taxi_process
def taxi_process(ident, trips, start_time = 0):
time = yield Event(start_time, ident, 'leave garage')
for i in range(trips):
time = yield Event(time, ident, 'pick up passenger')
time = yield Event(time, ident, 'drop off passenger')
yield Event(time, ident, 'going home')
#end taxi_process
#begin taxi_simulator
class Simulator:
def __init__(self, procs_map):
self.events = queue.PriorityQueue()
self.procs = dict(procs_map)
def run(self, end_time):
for _, proc in sorted(self.procs.items()):
first_event = next(proc)
self.events.put(first_event)
sim_time = 0
while sim_time < end_time:
if self.events.empty():
print('*** end of events ***')
break
current_event = self.events.get()
sim_time, proc_id, previous_action = current_event
print('taxi:', proc_id, proc_id*' ', current_event)
active_proc = self.procs[proc_id]
next_time = sim_time + compute_duration(previous_action)
try:
next_event = active_proc.send(next_time)
except StopIteration:
del self.procs[proc_id]
else:
self.events.put(next_event)
else:
msg = '***end of simulation time: {} events pending ***'
print(msg.format(self.events.qsize()))
def compute_duration(previous_action):
if previous_action in ['leave garage', 'drop off passenger']:
interval = SEARCH_DURATION
elif previous_action == 'pick up passenger':
interval = TRIP_DURATION
elif previous_action == 'going home':
interval = 1
else:
raise ValueError('unknown previous_action: {}'.format(previous_action))
return int(random.expovariate(1/interval))+1
def main(end_time=DEFAULT_END_TIME, num_taxis=DEFAULT_NUMBER_OF_TAXTIS, seed=None):
if seed is not None:
random.seed(seed)
taxis = {i: taxi_process(i, (i+1*2), i*DEPARTURE_INTERVAL) for i in range(num_taxis)}
sim = Simulator(taxis)
sim.run(end_time)
if __name__ == '__main__':
main()
结果:
taxi: 0 Event(time=0, proc=0, action='leave garage')
taxi: 0 Event(time=5, proc=0, action='pick up passenger')
taxi: 1 Event(time=5, proc=1, action='leave garage')
taxi: 1 Event(time=6, proc=1, action='pick up passenger')
taxi: 0 Event(time=8, proc=0, action='drop off passenger')
taxi: 2 Event(time=10, proc=2, action='leave garage')
taxi: 2 Event(time=11, proc=2, action='pick up passenger')
taxi: 0 Event(time=13, proc=0, action='pick up passenger')
taxi: 1 Event(time=16, proc=1, action='drop off passenger')
taxi: 0 Event(time=17, proc=0, action='drop off passenger')
taxi: 1 Event(time=17, proc=1, action='pick up passenger')
taxi: 2 Event(time=18, proc=2, action='drop off passenger')
taxi: 0 Event(time=20, proc=0, action='going home')
taxi: 2 Event(time=32, proc=2, action='pick up passenger')
taxi: 1 Event(time=38, proc=1, action='drop off passenger')
taxi: 1 Event(time=42, proc=1, action='pick up passenger')
taxi: 1 Event(time=58, proc=1, action='drop off passenger')
taxi: 1 Event(time=62, proc=1, action='going home')
taxi: 2 Event(time=88, proc=2, action='drop off passenger')
taxi: 2 Event(time=89, proc=2, action='pick up passenger')
taxi: 2 Event(time=122, proc=2, action='drop off passenger')
taxi: 2 Event(time=123, proc=2, action='pick up passenger')
taxi: 2 Event(time=130, proc=2, action='drop off passenger')
taxi: 2 Event(time=141, proc=2, action='going home')
*** end of events ***
Process finished with exit code 0
上面的实例,在设计模式上,可以用面向对象的模式编程,为每个出租车构建一个实例,实例中有 出库 上车 下车 回家的方法,可以选择多线程运行,互相不干扰, 也可以建立一个优先级列表进行单线程处理。倘若运行的实例足够多,因为协程是yield进行的生成器 而不是众多实例 所以对内存的占用是有很大优势的, 而且单线程运行 不需要CPU 内存进行上下文切换,节约了计算资源。 yield from 是一个加强版,可以处理诸如Stopiteraton 的错误,简化了编程。