[英]Best way to receive the 'return' value from a python generator
自 Python 3.3 起,如果生成器 function 返回一個值,該值將成為引發的 StopIteration 異常的值。 這可以通過多種方式收集:
yield from
表達式的值,這意味着封閉的 function 也是一個生成器。next()
或.send()
的調用。但是,如果我只是想在 for 循環中迭代生成器(最簡單的方法),似乎沒有辦法收集 StopIteration 異常的值,從而收集返回值。 我使用一個簡單的示例,其中生成器生成值,並在最后返回某種摘要(運行總計、平均值、時間統計等)。
for i in produce_values():
do_something(i)
values_summary = ....??
一種方法是自己處理循環:
values_iter = produce_values()
try:
while True:
i = next(values_iter)
do_something(i)
except StopIteration as e:
values_summary = e.value
但這拋棄了 for 循環的簡單性。 我不能使用yield from
因為這要求調用代碼本身就是一個生成器。 有沒有比上面顯示的 roll-ones-own for 循環更簡單的方法?
您可以將StopIteration
的value
屬性(可以說是StopIteration
本身)視為實現細節,而不是設計用於“正常”代碼中。
看看PEP 380 ,它指定了 Python 3.3 的特性的yield from
:它討論了使用StopIteration
來攜帶返回值的一些替代方案。
由於您不應該在普通的for
循環中獲取返回值,因此它沒有語法。 與您不應該明確捕獲StopIteration
方式相同。
對於您的情況,一個不錯的解決方案是一個小型實用程序類(可能對標准庫足夠有用):
class Generator:
def __init__(self, gen):
self.gen = gen
def __iter__(self):
self.value = yield from self.gen
這將包裝任何生成器並捕獲其返回值以供稍后檢查:
>>> def test():
... yield 1
... return 2
...
>>> gen = Generator(test())
>>> for i in gen:
... print(i)
...
1
>>> print(gen.value)
2
您可以制作一個輔助包裝器,它會捕獲StopIteration
並為您提取值:
from functools import wraps
class ValueKeepingGenerator(object):
def __init__(self, g):
self.g = g
self.value = None
def __iter__(self):
self.value = yield from self.g
def keep_value(f):
@wraps(f)
def g(*args, **kwargs):
return ValueKeepingGenerator(f(*args, **kwargs))
return g
@keep_value
def f():
yield 1
yield 2
return "Hi"
v = f()
for x in v:
print(x)
print(v.value)
處理返回值的一種輕量級方法(不涉及實例化輔助類)是使用依賴注入。
即,可以使用以下包裝器/幫助器生成器函數傳入函數以處理/作用於返回值:
def handle_return(generator, func):
returned = yield from generator
func(returned)
例如,以下——
def generate():
yield 1
yield 2
return 3
def show_return(value):
print('returned: {}'.format(value))
for x in handle_return(generate(), show_return):
print(x)
結果是 -
1
2
returned: 3
我能想到的最明顯的方法是用戶定義的類型,它會為你記住摘要..
>>> import random
>>> class ValueProducer:
... def produce_values(self, n):
... self._total = 0
... for i in range(n):
... r = random.randrange(n*100)
... self._total += r
... yield r
... self.value_summary = self._total/n
... return self.value_summary
...
>>> v = ValueProducer()
>>> for i in v.produce_values(3):
... print(i)
...
25
55
179
>>> print(v.value_summary)
86.33333333333333
>>>
另一種有時合適的輕量級方法是在每個生成器步驟中生成運行摘要,以及元組中的主要值。 循環保持簡單,有一個額外的綁定,之后仍然可用:
for i, summary in produce_values():
do_something(i)
show_summary(summary)
如果某人可以使用的不僅僅是最后一個匯總值,例如更新進度視圖,這將特別有用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.