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

In python is there a way to check if a function is a “generator function” before calling it?

假设我有两个功能:

1
2
3
4
5
def foo():
  return 'foo'

def bar():
  yield 'bar'

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

1
2
3
4
5
6
7
def run(func):
  if is_generator_function(func):
     gen = func()
     gen.next()
     #... run the generator ...
  else:
     func()

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

现在考虑以下情况:

1
2
3
4
5
def goo():
  if False:
     yield
  else:
     return

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

谢谢!


1
2
3
4
5
6
7
8
9
10
11
12
>>> import inspect
>>>
>>> def foo():
...   return 'foo'
...
>>> def bar():
...   yield 'bar'
...
>>> print inspect.isgeneratorfunction(foo)
False
>>> print inspect.isgeneratorfunction(bar)
True
  • 在新版本的Python 26


实际上,我只是wondering如何有用的这一假设的is_generator_function()会成为真的。则认为:

1
2
3
4
5
6
7
8
9
10
11
def foo():
    return 'foo'
def bar():
    yield 'bar'
def baz():
    return bar()
def quux(b):
    if b:
        return foo()
    else:
        return bar()

你应该为bazquuxis_generator_function()回报?但一个baz()returns生成器本身不是一个生成器,和quux()可能或可能不返回。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> 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        
>>>

正如你看到的,关键的区别是,bytecode为bar将包含在至少一个YIELD_VALUEopcode。我recommend dis(redirecting模块使用其输出到一个stringio instance和检查其getvalue,当然),因为这提供了一个措施,在你的robustness bytecode——准确numeric价值变化的研究opcodes将改变disassembled象征性价值,但会留下相当稳定;-)。


我已经在这一decorator implemented?胡克斯的decorated / yielded价值函数返回。按:其基本

1
2
3
4
5
6
7
8
9
10
11
12
13
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

它的功能是decorator作品因为unconditionnaly回报价值:这是被称为,是测试。

编辑:Robert lujo以下的评论,我结束了与一些如:

1
2
3
4
5
6
7
8
9
10
11
12
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