【问题标题】:Wrapping generator functions in Python在 Python 中包装生成器函数
【发布时间】:2011-09-18 20:54:46
【问题描述】:

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

以下是我想出的。如其所写,这将尝试遍历 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

【问题讨论】:

  • inspect 模块实际上非常简洁,仔细想想。

标签: python generator decorator


【解决方案1】:

好的,看看这个:

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

不过,这需要 Python 2.6 或更高版本。

【讨论】:

  • >>> isinstance(s, generator) Traceback(最近一次调用最后):文件“”,第 1 行,在 NameError: name 'generator' is not defined跨度>
  • @JAB:“生成器”是 python 3.2 的东西吗? "isinstance(func, generator)" 在 Python 2.7 中只是给出了一个 NameError
  • @cldy, Gerrat:抱歉,刚刚注意到即使类名是“生成器”,您也必须从 types 模块导入 GeneratorType 并使用它才能工作(更新我的回答反映了这一点)。不过,这很愚蠢,因为像isinstance(1, int) 这样的东西工作得很好。 (嗯,在 Python 2.2+ 中。)我想这是因为 int 等也是内置函数,但 generator 不是。
  • 该函数不应引发 StopIteration,它应返回一个迭代器,该迭代器在第一次 next 调用时引发 StopIteration,空列表可以正常工作。
  • 另外,这行不通。生成器函数是在调用时返回生成器的函数,函数本身不会是generator 类的实例。您将不得不使用isinstance(func(), GeneratorType),这违背了目的。
【解决方案2】:

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

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

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

【讨论】:

  • 删除了我的答案,因为它最终没用。看起来像在dis.dis(foo) 的结果中搜索YIELD_VALUE,正如您链接的那个问题中提到的那样,可能是最好的选择。顺便说一句,从技术上讲,埃里克已经在他的previous 集合中使用了一种记忆形式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-19
  • 1970-01-01
  • 2015-09-03
  • 2020-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多