我有一种情况,我需要挂钩某些函数,以便我可以检查返回值并跟踪它们 . 这对于跟踪方法/函数返回的值的运行平均值非常有用 . 但是,这些方法/功能也可以是生成器 .
但是,如果我没有错,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
并且这个装饰器中的 yield
是必要的,因为我需要跟踪值,因为调用者迭代这个装饰的生成器(即 "real-time" ) . 意思是,我不能简单地用 values = list(ret_value)
替换 for
和 yield
,并返回 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 . 但是,仍然不允许装饰器检查包装的生成器返回的每个值(只是第一个) . 这有解决方案,还是两种类型的装饰器(一个用于函数,一个用于装饰器)是必需的?
1 回答
一旦解决方案,我意识到:
我不知道这是否是唯一的,或者是否存在没有为发电机外壳制作单独功能的那个 .