【发布时间】:2019-05-10 05:49:33
【问题描述】:
我有一种情况,我需要挂钩某些函数,以便检查返回值并跟踪它们。这对于跟踪例如方法/函数返回的值的运行平均值很有用。但是,这些方法/函数也可以是生成器。
但是,如果我没记错的话,python 会在解析时检测生成器,并且在运行时调用该函数时,它总是返回一个生成器。因此我不能简单地做这样的事情:
import types
def decorator(func):
average = None # assume average can be accessed by other means
def wrap(*args, **kwargs):
nonlocal average
ret_value = func(*args, **kwargs)
#if False wrap is still a generator
if isinstance(ret_value, types.GeneratorType):
for value in ret_value:
# update average
yield value
else:
# update average
return ret_value # ret_value can't ever be fetched
return wrap
在这个装饰器中yielding 是必要的,因为我需要在调用者迭代这个装饰生成器时跟踪值(即“实时”) .意思是,我不能简单地将for 和yield 替换为values = list(ret_value),然后返回values。 (即)如果func 是一个生成器,它需要在装饰后保持为生成器。但是如果func 是一个纯函数/方法,即使else 被执行,wrap 仍然是一个生成器。这意味着,ret_value 永远无法获取。
使用这种生成器的玩具示例如下:
@decorated
def some_gen(some_list):
for _ in range(10):
if some_list[0] % 2 == 0:
yield 1
else:
yield 0
def caller():
some_list = [0]
for i in some_gen(some_list):
print(i)
some_list[0] += 1 # changes what some_gen yields
对于玩具示例,可能有更简单的解决方案,但这只是为了证明一点。
也许我遗漏了一些明显的东西,但我做了一些研究并没有找到任何东西。我找到的最接近的是this。但是,这仍然不能让装饰器检查包装生成器返回的每个值(只是第一个)。这是否有解决方案,或者是否需要两种类型的装饰器(一种用于功能,一种用于装饰器)?
【问题讨论】:
标签: python-3.x generator python-decorators