我遇到一种情况,需要挂接某些函数,以便可以检查返回值并跟踪它们。这对于跟踪例如方法/函数返回的值的运行平均值很有用。但是,这些方法/函数也可以是生成器。
但是,如果我没记错的话,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
,因为在调用者迭代此装饰的生成器时,我需要跟踪这些值(即 “实时” >)。意思是,我不能简单地将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。但是,这仍然不能让装饰器检查包装的生成器(只是第一个)返回的每个值。这是否有解决方案,还是需要两种类型的装饰器(一种用于功能,一种用于装饰器)?
答案 0 :(得分:0)
我意识到的解决方法是:
def as_generator(gen, avg_update):
for i in gen:
avg_update(i)
yield i
import types
def decorator(func):
average = None # assume average can be accessed by other means
def wrap(*args, **kwargs):
def avg_update(ret_value):
nonlocal average
#update average
pass
ret_value = func(*args, **kwargs)
#if False wrap is still a generator
if isinstance(ret_value, types.GeneratorType):
return as_generator(ret_value, avg_update)
else:
avg_update(ret_value)
return ret_value # ret_value can't ever be fetched
return wrap
我不知道这是不是唯一的一个,或者是否存在没有为生成器案例创建单独功能的一个。