我刚刚发现各种itertools函数返回的类类型不被Python类型系统视为生成器。
首先,设置:
import collections
import glob
import itertools
import types
ig = glob.iglob('*')
iz = itertools.izip([1,2], [3,4])
然后:
>>> isinstance(ig, types.GeneratorType)
True
>>> isinstance(iz, types.GeneratorType)
False
glob.iglob()
结果或任何其他典型的生成器属于types.GeneratorType
类型。但是itertools的结果不是。如果我想编写一个必须热切评估输入序列的函数,这会导致很多混乱 - 我需要知道它是否是生成器。
我发现了这个替代方案:
>>> isinstance(ig, collections.Iterator)
True
>>> isinstance(iz, collections.Iterator)
True
但这并不理想,因为iter(x)
是一个Iterator
,无论x
是一个具体的(热切评估的)序列,还是一个生成器(懒惰地评估)。
最终目标是这样的:
def foo(self, sequence):
"""Store the sequence, making sure it is fully
evaluated before this function returns."""
if isinstance(sequence, types.GeneratorType):
self.sequence = list(sequence)
else:
self.sequence = sequence
我想要这样做的一个例子是,如果对序列的评估可能会引发异常,并且我希望从foo()
引发该异常,而不是从{{1 }}
我不喜欢self.sequence
方法,因为它会产生一些误报 - 我不想不必要地构造输入列表的副本,因为它可能很大。
我愿意忽略“不寻常”的迭代器,这意味着如果有人实现了一个不符合生成器条件的自定义迭代器,但我不愿意为itertools做出错误的行为,因为它们是相当的受欢迎。
答案 0 :(得分:4)
将generators视为实施iterator的众多可能方式之一。 itertools都是用C语言编写的自定义迭代器。大部分都可以使用生成器使用较慢的代码实现,但它们是为速度而设计的。
types.GeneratorType被指定为“生成器 - 迭代器对象的类型,通过调用生成器函数生成。”由于glob.iglob()返回的迭代器是通过调用生成器函数生成的,因此它将匹配生成器类型。但是,itertools.izip()返回的迭代器是由C代码生成的,因此它与生成器类型不匹配。
换句话说, types.GeneratorType 对于识别所有延迟评估的迭代器没有用,它只对识别实际generator-iterators有用。
听起来目标是区分“热切评估”的集合(例如 list ,元组, dict 和设置)与“懒惰评估”迭代器。使用collections.Iterator可能是要走的路:
>>> isinstance([], collections.Iterator)
False
>>> isinstance((), collections.Iterator)
False
>>> isinstance({}, collections.Iterator)
False
>>> isinstance(set(), collections.Iterator)
False
>>> isinstance(iter([]), collections.Iterator)
True
>>> isinstance(iter(()), collections.Iterator)
True
>>> isinstance(iter({}), collections.Iterator)
True
>>> isinstance(iter(set()), collections.Iterator)
True
>>> isinstance(glob.iglob('.'), collections.Iterator)
True
>>> isinstance(itertools.izip('abc', 'def'), collections.Iterator)
True
>>> isinstance((x**2 for x in range(5)), collections.Iterator)
True
如果你已经在任何“渴望”的集合上调用了 iter(),那么在不诉诸诸如{{shenanigans之类的恶作剧的情况下弄清楚上游迭代的性质为时已晚。 1}}。
所述目标是“存储序列,确保在此函数返回之前对其进行全面评估”。执行此操作的常用方法是type(x) in {type(iter(s)) for s in ([], (), {}, set())}
,不进行周围检查以查看它是否已经是列表,元组,双端或其他一些完全评估的序列。这可能看起来很浪费,但list()调用非常快(它只是以C速度复制对象指针)。