【问题标题】:real-time decorator for functions and generators函数和生成器的实时装饰器
【发布时间】: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 是必要的,因为我需要在调用者迭代这个装饰生成器时跟踪值(即“实时”) .意思是,我不能简单地将foryield 替换为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


    【解决方案1】:

    我意识到的解决方案是:

    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
    

    我不知道这是否是唯一的,或者是否存在一个没有为生成器案例制作单独的函数。

    【讨论】:

      猜你喜欢
      • 2011-06-06
      • 2021-06-22
      • 2019-07-09
      • 2019-01-14
      • 1970-01-01
      • 2022-01-09
      • 1970-01-01
      • 2010-10-27
      • 2019-05-26
      相关资源
      最近更新 更多