我经常使用返回某个类的生成器。我想做的是对生成器类进行子类化,以便我可以在其上使用适合于生成该类实例的生成器的方法。例如,我想做的一件事就是有一个方法返回一个过滤基础生成器的生成器。
我想做这样的事情:
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
时遇到错误。
答案 0 :(得分: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
所以,只需使用它来构建你的生成器。
答案 1 :(得分: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...
答案 2 :(得分:4)
生成器的行为类似于迭代器,但它表示的序列与元组或列表不同,是为每个迭代步骤生成的。创建生成器的常用方法是使用生成器表达式或使用yield
语句;任何其他机制,如果它存在,是黑魔法,你应该远离它。
因此,您应该忘记types.GeneratorType
并继承它。您通常将发电机包裹或链接在一起。您可以使用生成器表达式执行此操作,就像在示例代码中一样,或者您可以使用精彩的itertools
standard module。