2014-05-16 50 views
1

装饰器是否可以将以下函数转换为生成器?使用装饰器将函数变成Python中的生成器

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

该功能可以根据需要进行修改。请注意,该函数没有yield语句,否则它已经是一个生成器。

+1

如果你真的疯了,你可以[在运行时修补字节码](http://www.jonathon-vogel.com/posts/patching_function_bytecode_with_python/) –

回答

7

如果你不能改变源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'] 

如果你可以改变功能,如果你想从countdown返回的东西(list或其他序列类型)和然后创建从返回的对象的发电机,decorator'd像

def decorator_that_makes_func_into_generator(func): 
    def wrapper(*a, **ka): 
     yield from func(*a, **ka) 
    return wrapper 

注:真棒yield from是在Python 3.3引入的,在旧版本的使用,而不是简单的循环:

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] 

没有什么阻止你将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] 
+0

不错!可能是因为他想要一个'return iter(func(* a,** ka))'对于包装函数来说足够了(它不会返回一个生成器,但也会被延迟)。 –

+0

@frostnational,你能记录代码来解释装饰器函数中发生了什么吗?这将真正帮助 – user2979872

+0

@ user2979872更新。 (我假设你知道装饰者是如何工作的) – vaultah

4

如果您无法更改该功能的主体,恐怕这太难了。

您试图包装的功能不是生成器,即使您将其包装到生成器中,该功能也会一次执行从头到尾执行。想必你不想那样。

理论上你能做什么?

  • 来看,它在调试器下
  • 跟踪下运行它,逐行
  • 接入功能源,对其进行修改,编译为字节码
  • 修改字节码
  • 倍率print()(更容易在Python3)
  • 使用无堆栈python在任意点保存和恢复堆栈