2009-12-09 143 views
48

可以說我有兩個功能:在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

回答

55
>>> 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

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),因爲這爲您提供了對字節碼更改的魯棒性度量 - 操作碼的確切數值將會改變,但反彙編的符號值將保持相當穩定;-)。

+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

14

其實,我想知道這樣一個假設的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

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 

它的工作原理,因爲裝飾功能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

相關問題