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

问题描述:

可以说我有两个功能:在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()函数有一个yield语句,我不知道是否可以轻松获取这些信息。

谢谢!

+1

值得注意的是有用的,如果一个函数包含'yield'声明,那么'回报'函数内的声明不允许有参数。它必须只是'返回',它终止了发电机。好问题! – 2009-12-09 05:13:29

+0

好点,'goo()'不应该是有效的,但它至少在这里(Python 2.6.2)。 – Carlos 2009-12-09 05:32:02

+4

给当前读者的一个说明:@GregHewgill上面的评论不再正确,现在你可以返回参数(它是在StopIteration的值attr处传递的) – wim 2014-03-14 01:58:40

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

只是2014年的一条评论,感谢你在2009年问题上提供2011年答案:) – wim 2014-03-14 02:07:19

+2

认为这解决了我的问题,但它并不完美。如果函数是一个封装的生成器,比如'partial(generator_fn,somearg = somevalue)',那么这个不会被检测到。 lambda x:generator_fun(x,somearg = somevalue)'也不会在类似的情况下使用lambda。这些实际上按预期工作;他的代码正在试验一个可以链式生成器的辅助函数,但是如果找到了一个正常的函数,它会将它包装在一个“单个项目生成器”中。 – 2016-08-25 18:38:12

>>> 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),因为这为您提供了对字节码更改的鲁棒性度量 - 操作码的确切数值将会改变,但反汇编的符号值将保持相当稳定;-)。

+0

Alex,你觉得叫“blah = func()“...然后检查type(blah)是否是生成器?如果不是,那么func()就已经被调用了:-)。我认为这应该是我首先会研究如何做到这一点的:-)。 – Tom 2009-12-09 05:13:48

+0

即将写入,但Pythonübergod首先出现。 :-) – paprika 2009-12-09 05:14:06

+0

在Q的标题中,OP非常清楚,他希望在调用**之前获得**信息 - 显示如何在**调用之后获得它**并且没有用明确表达的约束来回答给定的问题。 – 2009-12-09 05:21:36

其实,我想知道这样一个假设的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()回报bazquuxbaz()返回一个发生器,但不是一个本身,并且quux()可能会返回一个发生器或可能不会。

+1

@Greg,绝对 - 任何可调用的函数都可以返回一个可迭代的(可能特别是迭代器和超特定的生成器,或者可能不会)或其他东西(或者根本不产生任何异常),取决于关于论点,随机数字或月球的相位。生成器函数是一个可调用的函数,如果“如果它返回任何东西而不是提高,将返回一个生成器对象”(同时,并不是所有符合条件的可调用函数都是gnerator函数)。用例因此难以调制。 – 2009-12-09 05:34:17

+0

你说得对,这是一种假设的问题。它是在我阅读David Beazley关于协同程序的演示文稿时发布的:http://www.dabeaz.com/coroutines/ – Carlos 2009-12-09 05:36:34

+10

分别是False,True,False,False。它不是关于函数是否返回生成器实例,而是关于它是否是生成器函数! – wim 2014-03-14 02:05:15

,我实现了对装饰函数的返回/产生价值钩子装饰。它的基本云:

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 

它的工作原理,因为装饰功能unconditionnaly称为:这是测试的返回值。


编辑:继罗伯特·路约的评论,我结束了类似:

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 
+0

我有类似的情况,我得到了错误:SyntaxError:'返回'与发电机内的参数。当我想到它时,它看起来是合乎逻辑的,相同的功能在同一时间不能是正常的功能和发电机功能。这对你的情况真的有用吗? – 2015-09-03 16:04:52