为什么Python itertools不被归类为生成器(GeneratorType)?

时间:2016-06-24 13:59:56

标签: python types iterator generator itertools

我刚刚发现各种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做出错误的行为,因为它们是相当的受欢迎。

1 个答案:

答案 0 :(得分:4)

为什么Python itertools不被归类为生成器?

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()怎么办?

如果你已经在任何“渴望”的集合上调用了 iter(),那么在不诉诸诸如{{shenanigans之类的恶作剧的情况下弄清楚上游迭代的性质为时已晚。 1}}。

结束目标

所述目标是“存储序列,确保在此函数返回之前对其进行全面评估”。执行此操作的常用方法是type(x) in {type(iter(s)) for s in ([], (), {}, set())},不进行周围检查以查看它是否已经是列表,元组,双端或其他一些完全评估的序列。这可能看起来很浪费,但list()调用非常快(它只是以C速度复制对象指针)。