4

我经常使用返回某个类的生成器。我想做的是生成生成器类的子类,以便我可以在其上使用适合生成该类实例的生成器的方法。例如,我想做的一件事是有一个方法,该方法返回一个过滤器基础生成器的生成器。

我想做这样的事情:

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

4

3 回答 3

7
types.GeneratorType 

定义为:

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

如您所见,它不是常规的class.

现在,是什么让发电机特别?不多。要使用 ,generator protocol只需执行iterator protocol. 有一个不错的捷径:当您是发电机next()时,您可以免费获得。__iter__这正是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

因此,只需使用它来构建您的生成器。

于 2012-04-28T10:55:48.147 回答
5

正如您先前问题的评论中所指出的,返回生成器表达式通常是一个坏主意。引用PEP 289

...强烈鼓励用户在立即使用其参数的函数中使用生成器表达式。对于更复杂的应用程序,完整的生成器定义在范围、生命周期和绑定方面总是更明显。

本着上述精神,我建议:

  • __iter__通过定义(反过来,可以是生成器)使主类可迭代。
  • 定义get_xxx为迭代的生成器selfyield来自它的特定值

例子:

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...
于 2012-04-28T10:49:18.463 回答
4

生成器的行为类似于迭代器,但与元组或列表不同,它所代表的序列是为每个迭代步骤延迟生成的。创建生成器的常用方法是使用生成器表达式或使用yield语句;任何其他机制,如果存在的话,都是黑魔法,你应该远离它。

因此,您应该忘记types.GeneratorType并继承它。您通常将生成器包装或链接在一起。您可以使用生成器表达式来做到这一点,就像您在示例代码中所做的那样,或者您可以使用出色的itertools标准模块

于 2012-04-28T09:40:08.680 回答