【问题标题】:Why do the enumerate, zip, range types not belong to types.GeneratorType?为什么 enumerate、zip、range 类型不属于 types.GeneratorType?
【发布时间】:2019-03-28 22:25:13
【问题描述】:

Python 3 引入了在调用 range()zip() 时返回的类似生成器的对象。返回的对象就像一个生成器,可以迭代一次,但不能很好地“打印”,就像enumerate() 返回参数一样。

然而,我很困惑地看到它们是不同的对象类型并且不属于 types.GeneratorType,或者至少这是 types 模块所显示的。一个可以运行的函数,例如期望生成器不会检测到它们。他们的遗产是什么?它们是否属于主要的“生成器”结构,以便它们例如可以与其他生成器一起识别吗?

import types

a = [1,2,3]
b = [4,5,6]

# create some generator-type objects
obj_zip = zip(a,b)
obj_enu = enumerate(a)
obj_r = range(10)

print(type(obj_zip))
print(type(obj_enu))
print(type(obj_r))

# checking against types.GeneratorType returns False
print(isinstance(obj_zip,types.GeneratorType))
print(isinstance(obj_enu,types.GeneratorType))
print(isinstance(obj_r,types.GeneratorType))

# checking against their own distinct object types returns True
print(isinstance(obj_zip,zip))

【问题讨论】:

  • range 对象不是生成器,它们是不可变的序列类型;你可以反复迭代它们,所以它们绝对不应该是GeneratorType

标签: python python-3.x types generator


【解决方案1】:

并不意味着是一个完整的答案(ShadowRanger answer 已经解释了一切),而只是说明types.GeneratorType 确实是一个非常有限的类型,如types.py 所示来源:

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

它只限定生成器函数的类型。其他“类似生成器”的对象不使用 yield,因此它们不匹配。

【讨论】:

    【解决方案2】:

    the GeneratorType docs:

    types.GeneratorType

    生成器-迭代器对象的类型,由生成器函数创建。

    生成器函数是语言中的特定事物;它表示使用yieldyield from 的函数(或生成器表达式,它们只是内联生成器函数的简写)。它是迭代器集的一个子集(所有你可以调用next() 来获取新值的东西),它又是可迭代对象的一个​​子集(你可以调用iter() 来获取迭代器的所有东西;迭代器本身是可迭代的,其中iter(iterator) 表现为身份函数。

    基本上,如果您要测试“我可以循环吗?”,请测试 isinstance(obj, collections.abc.Iterable)。如果您正在检查“这是一个可耗尽的迭代器吗?” (也就是说,我会通过循环遍历它吗?),测试isinstance(obj, collections.abc.Iterator) 或基于鸭子类型的方法,测试iter(obj) is obj(迭代器上的不变量需要 iter(iterator)产生原始迭代器对象不变)。

    请注意,range 不是生成器或迭代器。每the docs

    范围实际上不是一个函数,而是一个不可变的序列类型,如 RangesSequence Types — list, tuple, range 中所述。

    作为不可变的序列类型意味着它可迭代的,但仅此而已。它通常被当作迭代器来使用这一事实是无关紧要的。如果它是一个迭代器,这里的第二个循环永远不会执行:

    r = range(3)
    for i in r:
        print("First", i)
    for i in r:
        print("Second", i)
    

    但它工作得很好,因为对 iter(r) 的每个(隐式)调用都会返回一个 new iterator 基于相同的底层 iterable em>。

    【讨论】:

    • 完美,这就是我想要的,Iterable、Iterator 和 Generator 之间的澄清。所以 enumerate()zip() 创建 iterators(即可以用尽)但不是 generators
    【解决方案3】:

    文档说 enumerate 在功能上等同于生成器。实际上它是用 C 实现的,并返回一个迭代器,而不是 Does enumerate() produce a generator object 中描述的生成器。 生成器和可迭代对象几乎相同。这在Difference between Python's Generators and Iterators中有详细解释。

    我假设您正在尝试解决一个真正的问题,例如找出是否可以迭代某些东西。要解决这个问题,您可以测试某个东西是否是 collections.Iterable 的实例。

    a = enumerate([1,2,3])
    isinstance(a, collections.Iterable)
    >>> True
    

    【讨论】:

    • “iterable”作为一个类别是“generator”的超集(所有生成器都是可迭代的,但并非所有可迭代的都是生成器),所以这并不是那么有用。
    • 好吧,我正在尝试创建一个在可迭代对象上运行两次的函数。因此,如果 iterable 是生成器,它会尝试克隆生成器(使用itertools.tee),但是如果传递的对象属于这些具有特殊类型的生成器类型对象,我在检查生成器时遇到问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-15
    • 2022-06-15
    • 1970-01-01
    • 2020-08-25
    • 1970-01-01
    • 2019-07-16
    • 2012-01-22
    相关资源
    最近更新 更多