2012-04-28 62 views
3

我經常使用一個返回某個類的生成器。我想要做的是對生成器類進行子類化,以便我可以使用適用於生成該類的實例的生成器的方法。例如,我想要做的事情之一就是返回一個過濾基本生成器的生成器的方法。是否有可能建議子類化發電機功能?

我想要做這樣的事情:

class Clothes(object): 
    def __init__(self, generator): 
     self.generator = generator 

    def get_red(self): 
     return (c for c in self.generator if c.color=="red") 

    def get_hats(self): 
     return (c for c in self.generator if c.headgear) 

衣服類我想當作衣服的集合。我沒有繼承一個系列的原因是,我很少想按原樣使用整套衣服,通常只需要進一步過濾。但是,我經常需要各種過濾的服裝。如果可能的話,我希望Clothes本身就是一個生成器,因爲這就是我打算使用它的原因,但是當嘗試子類types.GeneratorType時出現錯誤。

+1

一些示例代碼會很好.. – 2012-04-28 08:45:21

+0

是什麼問題?您是否嘗試覆蓋生成器方法?發生了什麼? – newtover 2012-04-28 08:53:31

回答

4

生成器的行爲就像一個迭代器,但它所表示的序列不像元組或列表,它在每個迭代步驟中都會生成懶惰。創建生成器的常用方法是使用生成器表達式或yield語句;任何其他機制,如果存在的話,都是黑魔法,你應該遠離它。

因此,你應該忘記types.GeneratorType並繼承它。您通常會將發電機包裝或鏈接在一起您可以使用生成器表達式來完成此操作,就像您在示例代碼中所做的那樣,或者您可以使用精彩的itertools standard module

5

正如您在前面的問題的評論中指出的那樣,返回生成器表達式通常是一個糟糕的主意。要引用PEP 289

...應強烈建議用戶在使用函數內的生成器表達式時立即使用它們的參數。對於更復雜的應用程序,完整的生成器定義在範圍,生命週期和綁定方面總是顯而易見的。

在上述的精神,我建議:

  • 使主類可迭代,通過定義__iter__(其又可以是一個發電機)。
  • 限定get_xxx作爲遍歷self發電機和yield特定值從它

實施例:

class Numbers(object): 

    def __iter__(self): 
     for x in range(10): 
      yield x 

    def get_odd(self): 
     for x in self: 
      if x & 1: 
       yield x 


nums = Numbers() 

for x in nums: 
    print x # 0 1 2 3... 

for x in nums.get_odd(): 
    print x # 1 3 5... 
7
types.GeneratorType 

定義爲:

def _g(): 
    yield 1 
GeneratorType = type(_g()) 

你看,它不是一個普通的class

現在,什麼使發電機特別? 並不多。 要使用generator protocol,只需要執行iterator protocol。 有一個很好的捷徑:當您的__iter__是一個發電機時,您可以免費獲得next()。 而這collections.Iterable究竟是如何定義的:

class Iterable(metaclass=ABCMeta): 

    @abstractmethod 
    def __iter__(self): 
     while False: 
      yield None 

    @classmethod 
    def __subclasshook__(cls, C): 
     if cls is Iterable: 
      if any("__iter__" in B.__dict__ for B in C.__mro__): 
       return True 
     return NotImplemented 

所以,僅僅用它來建立自己的發電機。