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

假设我有两个功能:

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()函数有一个屈服语句,并且我想知道是否可以轻松获得该信息。

谢谢!

11900 次浏览
>>> 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,当然) ,因为这为您提供了对字节码更改的健壮性度量——操作码的确切数值将发生变化,但被分解的符号值将保持相当稳定; ——)。

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

我已经实现了一个装饰器,它钩住修饰后的返回/生成值函数,其基本原理是:

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