假设我有两个功能:
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语句,我想知道是否可以轻松获取该信息。
谢谢!
答案 0 :(得分:62)
>>> import inspect
>>>
>>> def foo():
... return 'foo'
...
>>> def bar():
... yield 'bar'
...
>>> print inspect.isgeneratorfunction(foo)
False
>>> print inspect.isgeneratorfunction(bar)
True
答案 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