2012-04-28 55 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 

所以,仅仅用它来建立自己的发电机。