如何理解 python 协程中的 `yield from`?
How to understand `yield from` in python coroutine?
代码来自 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)
这让我想起了 nice 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
时,它会引发一个 StopIterationException
,值为 local_var
。 (这就是从生成器返回的结果)。
这是外部生成器:
def grouper(results, key):
while True:
results[key] = yield from averager()
this所做的是将内部生成器的产量暴露给调用代码,until 内部生成器引发 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
) 与内部生成器通信。
- 它 'ends' 内部生成器通过发送
None
,此时第一个 yield from
语句结束,并分配向上传递的值。
- 此时,外部生成器准备发送另一个值
- 循环继续,生成器被垃圾回收删除。
what's with the while True:
loop?
考虑这段代码,它也适用于外部生成器:
def grouper(result, key):
result[key] = yield from averager
yield 7
唯一重要的是生成器不应该被耗尽,所以它不会在链上传递异常说 'I have nothing left to iterate'。
P.S。使困惑?我曾是。我不得不检查一下,自从我尝试使用基于生成器的 coros 以来已经有一段时间了。它们计划删除---使用 asyncio,它多 更好。
代码来自 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)
这让我想起了 nice 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
时,它会引发一个 StopIterationException
,值为 local_var
。 (这就是从生成器返回的结果)。
这是外部生成器:
def grouper(results, key):
while True:
results[key] = yield from averager()
this所做的是将内部生成器的产量暴露给调用代码,until 内部生成器引发 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
) 与内部生成器通信。 - 它 'ends' 内部生成器通过发送
None
,此时第一个yield from
语句结束,并分配向上传递的值。 - 此时,外部生成器准备发送另一个值
- 循环继续,生成器被垃圾回收删除。
what's with the
while True:
loop?
考虑这段代码,它也适用于外部生成器:
def grouper(result, key):
result[key] = yield from averager
yield 7
唯一重要的是生成器不应该被耗尽,所以它不会在链上传递异常说 'I have nothing left to iterate'。
P.S。使困惑?我曾是。我不得不检查一下,自从我尝试使用基于生成器的 coros 以来已经有一段时间了。它们计划删除---使用 asyncio,它多 更好。