如何理解python协同程序中的“yield from”?

2024-10-01 09:23:23 发布

您现在位置:Python中文网/ 问答频道 /正文

代码来自表Fluent Python 1st edtion

我无法理解grouper中的while True:行,删除该行将引发StopIteration错误

但是我发现一个新版本的grouper没有while True:可以工作。为什么group.send(None)需要while True:(或另一个results[key] = yield from averager())中的另一个循环

我的理解是group.send(None)将停止yield from averager()并为results[key]分配一个值(Result(count, average))。就这些

from collections import namedtuple

Result = namedtuple('Result', 'count average')


# the subgenerator
def averager():  # <1>
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield  # <2>
        if term is None:  # <3>
            break
        total += term
        count += 1
        average = total/count
    return Result(count, average)  # <4>


# the delegating generator
def grouper(results, key):  # <5>
    while True:  # <6>
        results[key] = yield from averager()  # <7>

# Another version works
#def grouper(results, key):
#    results[key] = yield from averager()
#    results[key] = yield from averager()

# the client code, a.k.a. the caller
def main(data):  # <8>
    results = {}
    for key, values in data.items():
        group = grouper(results, key)  # <9>
        next(group)  # <10>
        for value in values:
            group.send(value)  # <11>
        group.send(None)  # important! <12>

    # print(results)  # uncomment to debug
    report(results)


# output report
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.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
    'girls;m':
        [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
    'boys;kg':
        [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
    'boys;m':
        [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}


if __name__ == '__main__':
    main(data)

Tags: keyfromnonesendtruedefcountgroup
1条回答
网友
1楼 · 发布于 2024-10-01 09:23:23

这让我想起了ascynio有多好,以及为什么每个人都应该使用它

通过遍历迭代器的操作可以最好地解释正在发生的事情。这是内部生成器,简化为:

def averager():
    local_var
    while True:
        term = yield
        if term is None:
            break
        local_var = do_stuff(term)
    return local_var

这有两件事。首先,它通过yield获取一些数据(呃,解释单词的选择很混乱),只要这些数据不是None。然后,当它None时,它会引发一个值为local_varStopIterationException。(这就是从发电机返回的功能)

这是外部发电机:

def grouper(results, key):
    while True:
        results[key] = yield from averager()

这个所做的是将内部生成器的产量公开给调用代码,直到内部生成器引发StopIterationException,它被静默地捕获(由yield from语句)并分配。然后它准备再次做同样的事情

然后我们有了调用代码:

def main(data):
    results = {}
    for key, values in data.items():
        group = grouper(results, key)
        next(group)
        for value in values:
            group.send(value)
        group.send(None)

这个的作用是:

  • 它只迭代外部生成器一次
  • 这将公开内部生成器的产量,并使用该产量(.send)与内部生成器通信
  • 它通过发送None(第一个yield from语句在该点结束)来“结束”内部生成器,并分配向上传递的值
  • 此时,外部生成器准备发送另一个值
  • 循环继续,生成器被垃圾回收删除

what's with the while True: loop?

考虑此代码,它也适用于外部生成器:

def grouper(result, key):
    result[key] = yield from averager
    yield 7

唯一重要的是,生成器不应该耗尽,因此它不会向链上传递一个异常,说“我没有什么可迭代的了”

p.S.困惑吗?我是。我必须检查一下,我已经有一段时间没有尝试使用基于发电机的coros了。他们被安排删除-使用asyncio,这比使用asyncio好得多

相关问题 更多 >