首页 文章

功能和生成器的实时装饰器

提问于
浏览
0

我有一种情况,我需要挂钩某些函数,以便我可以检查返回值并跟踪它们 . 这对于跟踪方法/函数返回的值的运行平均值非常有用 . 但是,这些方法/功能也可以是生成器 .

但是,如果我没有错,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) 替换 foryield ,并返回 values . (即)如果 func 是发电机,它一旦装饰就需要保持发电机状态 . 但如果 func 是一个纯函数/方法,即使执行 elsewrap 仍然是一个生成器 . 意思是, 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 回答

  • 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
    

    我不知道这是否是唯一的,或者是否存在没有为发电机外壳制作单独功能的那个 .

相关问题