在Python中包装生成器函数

时间:2011-06-20 16:50:48

标签: python generator decorator

我正在编写一些遍历可能具有循环引用的结构的代码。 我没有在递归函数的开头显式地进行检查,而是认为我会创建一个装饰器,它不允许使用相同的参数多次调用函数。

以下是我的想法。在编写时,这将尝试迭代Nonetype并引发异常。我知道我可以通过返回说出一个空列表来修复它,但我想要更优雅。有没有办法从装饰器内判断被装饰的函数是否是生成函数?这样我可以有条件地提高StopIteration,如果它是一个生成器,或者只返回None。

previous = set()
def NO_DUPLICATE_CALLS(func):
    def wrapped(*args, **kwargs):
        if args in previous:
            print 'skipping previous call to %s with args %s %s' % (func.func_name, repr(args), repr(kwargs))
            return
        else:
            ret = func(*args, **kwargs)
            previous.add(args)
            return ret
    return wrapped

@NO_DUPLICATE_CALLS
def foo(x):
    for y in x:
        yield y

for f in foo('Hello'):
    print f

for f in foo('Hello'):
    print f

2 个答案:

答案 0 :(得分:5)

好的,请看看:

>>> from inspect import isgeneratorfunction
>>> def foo(x):
...    for y in x:
...        yield y
...
>>> isgeneratorfunction(foo)
True

但这需要Python 2.6或更高版本。

答案 1 :(得分:4)

不幸的是,如果一个函数在没有调用它的情况下返回某种类型的可迭代函数并没有真正好的方法,请参阅this answer另一个问题以获得对某些潜在问题的非常好的解释。

但是,您可以使用修改后的memoize装饰器来解决这个问题。通常,memoizing装饰器会创建一个带有先前参数返回值的缓存,但是不是存储完整值,而是可以存储返回值的类型。当您遇到参数时,您已经看到只返回该类型的新初始化,这将导致空字符串,列表等。

这是一个memoize装饰器的链接,以帮助您入门:
http://wiki.python.org/moin/PythonDecoratorLibrary#Memoize