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

在Python中使用装饰器将函数转换为生成器,python,generator,decorator,Python,Generator,Decorator,装饰师有没有办法将下面的函数转换成生成器 @decorator_that_makes_func_into_generator def countdown(n): while n > 0: print n, n = n - 1 如有必要,可修改该功能。请注意,该函数没有yield语句,否则它将已经是一个生成器。如果无法更改倒计时的源代码,则必须捕获输出: import sys from io import StringIO def decorato

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

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

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

如果无法更改
倒计时的源代码,则必须捕获输出:

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']
注意:Python3.3中引入了来自
的awesome
收益,在较旧的版本中使用普通循环:

for x in func(*a, **ka): 
    yield x
例如:

@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]

如果无法更改
倒计时
的源
,则必须捕获输出:

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']
注意:Python3.3中引入了来自
的awesome
收益,在较旧的版本中使用普通循环:

for x in func(*a, **ka): 
    yield x
例如:

@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]

如果你不能改变函数的主体,恐怕这太难了

您尝试包装的函数不是生成器,即使您将其包装到生成器中,此函数也将从头到尾一次性执行。大概你不想这样吧

理论上你能做什么

  • 在调试器下运行它
  • 逐行跟踪
  • 访问函数源代码,修改它,编译成字节码
  • 修改字节码
  • 重写
    print()
    (在Python3中更容易)
  • 使用无堆栈python在任意点保存和恢复堆栈

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

      您尝试包装的函数不是生成器,即使您将其包装到生成器中,此函数也将从头到尾一次性执行。大概你不想这样吧

      理论上你能做什么

      • 在调试器下运行它
      • 逐行跟踪
      • 访问函数源代码,修改它,编译成字节码
      • 修改字节码
      • 重写
        print()
        (在Python3中更容易)
      • 使用无堆栈python在任意点保存和恢复堆栈

      不错!也许对于他想要的,一个
      返回iter(func(*a,**ka))
      对于包装器函数来说就足够了(它不会返回生成器,但也会被延迟)。@frostnational,你能记录代码来解释decorator函数中发生了什么吗。这真的会help@user2979872更新。(我想你知道装修工是怎么工作的)很好!也许对于他想要的,一个
      返回iter(func(*a,**ka))
      对于包装器函数来说就足够了(它不会返回生成器,但也会被延迟)。@frostnational,你能记录代码来解释decorator函数中发生了什么吗。这真的会help@user2979872更新。(我想你知道装潢师是怎么工作的)如果你真的疯了,你总是可以如果你真的疯了,你总是可以