使用装饰器将函数转换为Python中的生成器

use*_*872 1 python generator decorator

装饰器有没有办法将下面的函数转换为生成器?

@decorator_that_makes_func_into_generator
def countdown(n):
    while n > 0:
        print n,
        n = n - 1
Run Code Online (Sandbox Code Playgroud)

必要时可以修改该功能.请注意,该函数没有yield语句,否则它已经是一个生成器.

vau*_*tah 8

如果你不能改变源countdown,你必须捕获输出:

import sys
from io import StringIO

def decorator_that_makes_func_into_generator(func):
    def wrapper(*a, **ka):
        # Temporarily redirect all output to StringIO instance (intr)
        ts, intr = sys.stdout, StringIO()
        sys.stdout = intr
        func(*a, **ka)
        # Restore normal stdout from backup (ts)
        sys.stdout = ts
        # Get output from intr, split it by whitespace and use it as generator
        yield from intr.getvalue().split()

    return wrapper

@decorator_that_makes_func_into_generator
def countdown(n):
    while n > 0:
        print(n)
        n = n - 1

print(countdown(5), list(countdown(5)))
# <generator object wrapper at 0x01E09058> ['5', '4', '3', '2', '1']
Run Code Online (Sandbox Code Playgroud)

如果你可以更改函数,如果你想从countdown(list或其他序列类型)返回一些东西,然后从返回的对象创建一个生成器,装饰器看起来像

def decorator_that_makes_func_into_generator(func):
    def wrapper(*a, **ka):
        yield from func(*a, **ka)
    return wrapper
Run Code Online (Sandbox Code Playgroud)

注意:真棒yield from是在Python 3.3中引入的,旧版本使用普通循环代替:

for x in func(*a, **ka): 
    yield x
Run Code Online (Sandbox Code Playgroud)

例:

@decorator_that_makes_func_into_generator
def countdown(n):
    res = []
    while n > 0:
        res.append(n)
        n = n - 1
    return res

print(type(countdown(5)), list(countdown(5))) 
# Output: <class 'generator'> [5, 4, 3, 2, 1]
Run Code Online (Sandbox Code Playgroud)

没有什么能阻止你申请decorator_that_makes_func_into_generator发电机:

@decorator_that_makes_func_into_generator
def countdown(n):
    while n > 0:
        yield n
        n = n - 1

print(type(countdown(5)), list(countdown(5))) 
# Outputs <class 'generator'> [5, 4, 3, 2, 1]
Run Code Online (Sandbox Code Playgroud)


Dim*_*nek 5

如果您不能更改该函数的主体,恐怕这太难了。

您尝试包装的函数不是生成器,即使将其包装到生成器中,该函数也会从头到尾地一次性执行。想必您不想要那样。

理论上你能做什么?

  • 在调试器下运行它
  • 逐行运行它
  • 访问函数源,对其进行修改,然后编译为字节码
  • 修改字节码
  • 覆盖print()(在Python3中更轻松)
  • 使用无堆栈python在任意点保存和恢复堆栈