在python中有一种方法可以在调用之前检查函数是否是“生成器函数”?

时间:2009-12-09 05:05:11

标签: python function generator coroutine

假设我有两个功能:

def foo():
  return 'foo'

def bar():
  yield 'bar'

第一个是普通函数,第二个是生成函数。现在我想写这样的东西:

def run(func):
  if is_generator_function(func):
     gen = func()
     gen.next()
     #... run the generator ...
  else:
     func()

is_generator_function()的直接实现是什么样的?使用types包我可以测试gen是否是生成器,但我希望在调用func()之前这样做。

现在考虑以下情况:

def goo():
  if False:
     yield
  else:
     return

调用goo()将返回生成器。我假设python解析器知道goo()函数有一个yield语句,我想知道是否可以轻松获取该信息。

谢谢!

4 个答案:

答案 0 :(得分:62)

>>> import inspect
>>> 
>>> def foo():
...   return 'foo'
... 
>>> def bar():
...   yield 'bar'
... 
>>> print inspect.isgeneratorfunction(foo)
False
>>> print inspect.isgeneratorfunction(bar)
True
  • Python 2.6版中的新功能

答案 1 :(得分:15)

实际上,我想知道这样一个假设的is_generator_function()是多么有用。考虑:

def foo():
    return 'foo'
def bar():
    yield 'bar'
def baz():
    return bar()
def quux(b):
    if b:
        return foo()
    else:
        return bar()

is_generator_function()baz应该quux返回什么? baz()返回一个生成器,但不是一个生成器,quux()可能会返回生成器,也可能不返回。

答案 2 :(得分:7)

>>> def foo():
...   return 'foo'
... 
>>> def bar():
...   yield 'bar'
... 
>>> import dis
>>> dis.dis(foo)
  2           0 LOAD_CONST               1 ('foo')
              3 RETURN_VALUE        
>>> dis.dis(bar)
  2           0 LOAD_CONST               1 ('bar')
              3 YIELD_VALUE         
              4 POP_TOP             
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE        
>>> 

如您所见,关键区别在于bar的字节码将包含至少一个YIELD_VALUE操作码。我建议使用dis模块(当然将其输出重定向到StringIO实例并检查其getvalue),因为这样可以衡量字节码更改的稳健性 - 操作码的确切数值会改变,但反汇编的象征价值会保持稳定; - )。

答案 3 :(得分:1)

我已经实现了一个挂钩装饰函数返回/生成值的装饰器。它的基本内容是:

import types
def output(notifier):
    def decorator(f):
        def wrapped(*args, **kwargs):
            r = f(*args, **kwargs)
            if type(r) is types.GeneratorType:
                for item in r:
                    # do something
                    yield item
            else:
                # do something
                return r
    return decorator

它的工作原理是因为装饰器函数是无条件调用的:它是被测试的返回值。


编辑:在Robert Lujo的评论之后,我最终得到了类似的内容:

def middleman(f):
    def return_result(r):
        return r
    def yield_result(r):
        for i in r:
            yield i
    def decorator(*a, **kwa):
        if inspect.isgeneratorfunction(f):
            return yield_result(f(*a, **kwa))
        else:
            return return_result(f(*a, **kwa))
    return decorator