2013-10-21 105 views
5

似乎有在Python兩種方式來測試是否對象是發電機:isinstance(foo,types.GeneratorType)或inspect.isgenerator(foo)?

import types 
isinstance(foo, types.GeneratorType) 

或:

import inspect 
inspect.isgenerator(foo) 

在「精神應該有one--和優選地僅一種 - 明顯的方式來做到這一點「,是其中一種方式推薦的方式(大概他們做同樣的事情......如果沒有,請賜教!)?

+0

爲什麼你想要檢查它呢? – ThiefMaster

+0

isgenerator我認爲更多的是一個函數(這沒有被稱爲),而isinstance通常是指一個instanciated生成器已返回或構建的東西(這就是我的2C在這件事上) –

+0

@ThiefMaster:公平的問題,顯然類型檢查到在Python中避免。我有一個函數,在99%的時間內通過一個列表迭代兩次。在發電機通過的1%時間內,我需要先將它列爲清單,然後再做這個清單。 – HorseloverFat

回答

6

他們是100%相同的:

>>> print(inspect.getsource(inspect.isgenerator)) 
def isgenerator(object): 
    """Return true if the object is a generator. 

    Generator objects provide these attributes: 
     __iter__  defined to support interation over container 
     close   raises a new GeneratorExit exception inside the 
         generator to terminate the iteration 
     gi_code   code object 
     gi_frame  frame object or possibly None once the generator has 
         been exhausted 
     gi_running  set to 1 when generator is executing, 0 otherwise 
     next   return the next item from the container 
     send   resumes the generator and "sends" a value that becomes 
         the result of the current yield-expression 
     throw   used to raise an exception inside the generator""" 
    return isinstance(object, types.GeneratorType) 

我會說,使用isinstance(object, types.GeneratorType)應該是首選方法,因爲它更清晰和簡單。 另外inspect.isgenerator僅在python2.6中添加,這意味着使用isinstance更向後兼容。

他們可能增加了isgenerator函數對稱isgeneratorfunction,它做了一些不同的事情。

+2

+1提醒我在'inspect'模塊中'getsource'函數。這很酷! – HorseloverFat

+0

我懷疑它被添加的原因是它可以作爲第二個參數傳遞給'inspect.getmembers',允許你遍歷一個對象屬性,只找到(說)生成器對象。 – SingleNegationElimination

+0

@dequestarmappartialsetattr可能是。另一個顯而易見的選擇是使用'partial',但爲了只傳遞第二個參數,你必須使用關鍵字args,我認爲它不被'isinstance'支持,因此唯一的另一種方法是使用一個'lambda'(與傳遞'isgenerator'完全相同)。 – Bakuriu

2

你可以做類型檢查,但你可能不想檢查只是發電機。你真正想要的是檢查'迭代器',或者說,你需要兩個迭代器。

import collections, itertools 

def cheap_tee(foo): 
    if isinstance(foo, collections.Iterator): 
     # this is already an iterator, we need to 'tee' it 
     return itertools.tee(foo) 
    elif isinstance(foo, collections.Iterable): 
     # this is already an iterable, get two iterators from it: 
     return iter(foo), iter(foo) 
    raise TypeError("Don't know how to cheaply copy these", foo) 

這將然後任何即遠程迭代,不只是生成器表達式工作。某些類型將提供自定義迭代器,這些自定義迭代器在數據結構上工作,這些數據結構不容易用生成器表達式或生成器表示,或者在C中作爲迭代器實現。也可以提供__copy__機制,itertools.tee實際上可以使用,並且不會複製工作,或者。只有當它的'真的已經是一個迭代器,tee不能爲你複製它將使用空間,爲你做所有的結晶。

1

,你應該能夠做到:

try: 
    x = possible_generator.next() 
    mylist = [x] + list(possible_generator) 

except: 
    pass 

這將發電機和內置iterables區分;然而,如果你有一個類似列表的自定義類,但是也會實現下一個,那麼它會失敗。

+0

這也將生成器轉換爲列表,這種類型首先破壞了使用生成器的目的。 – chepner

+0

但這就是他想要做的......他會走兩遍,所以如果它已經是一個列表,請保留原文,否則將生成器壓扁成列表。 –

+0

我錯過了那個評論。 – chepner

相關問題