yield from 的特性

本文探讨了Python中`yield from`的用法,强调它如何简化嵌套生成器的代码,处理`StopIteration`异常,并在协程交互中扮演角色。通过一个出租车仿真的例子,说明了使用协程(特别是`yield from`)的优势,如减少内存占用和避免上下文切换,提高了计算效率。
摘要由CSDN通过智能技术生成
  1. 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是协程运行到末端进行的报错。

  1. 用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循环一样, 会自己处理退出异常,不需要自己实现。

  1. 协程的意义
    看下面一个出租车仿真编程
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 的错误,简化了编程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值