enumerate()是否生成生成器对象?

Jos*_*ien 31 python

作为一个完整的Python新手,它肯定是这样的.运行以下...

x = enumerate(['fee', 'fie', 'foe'])
x.next()
# Out[1]: (0, 'fee')

list(x)
# Out[2]: [(1, 'fie'), (2, 'foe')]

list(x)
# Out[3]: []
Run Code Online (Sandbox Code Playgroud)

......我注意到:(a)x确实有一个next方法,似乎是生成器所需要的,而(b)x只能迭代一次,这个着名的python标签答案中强调了生成器的一个特征.

在另一方面,这两个最高度upvoted回答这个问题, 如何确定一个对象是否是发电机似乎表明,enumerate()不会返回发电机.

import types
import inspect

x = enumerate(['fee', 'fie', 'foe'])

isinstance(x, types.GeneratorType)
# Out[4]: False

inspect.isgenerator(x)
# Out[5]: False
Run Code Online (Sandbox Code Playgroud)

......虽然对该问题的第三个低调的回答似乎表明实际上enumerate() 确实返回了一个发电机:

def isgenerator(iterable):
    return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__')

isgenerator(x)
# Out[8]: True
Run Code Online (Sandbox Code Playgroud)

发生什么了?是x发电机还是没有?它在某种意义上是"类似发电机",但不是真正的发电机吗?Python使用duck-typing是否意味着上面最后一个代码块中概述的测试实际上是最好的?

我不会继续写下我头脑中可能存在的可能性,而是将其抛给那些立即知道答案的人.

dan*_*ano 33

虽然Python文档说它enumerate在功能上等同于:

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1
Run Code Online (Sandbox Code Playgroud)

enumerate函数返回迭代器,但不是实际的生成器.如果help(x)在创建enumerate对象后调用,则可以看到此信息:

>>> x = enumerate([1,2])
>>> help(x)
class enumerate(object)
 |  enumerate(iterable[, start]) -> iterator for index, value of iterable
 |  
 |  Return an enumerate object.  iterable must be another object that supports
 |  iteration.  The enumerate object yields pairs containing a count (from
 |  start, which defaults to zero) and a value yielded by the iterable argument.
 |  enumerate is useful for obtaining an indexed list:
 |      (0, seq[0]), (1, seq[1]), (2, seq[2]), ...
 |  
 |  Methods defined here:
 |  
 |  __getattribute__(...)
 |      x.__getattribute__('name') <==> x.name
 |  
 |  __iter__(...)
 |      x.__iter__() <==> iter(x)
 |  
 |  next(...)
 |      x.next() -> the next value, or raise StopIteration
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __new__ = <built-in method __new__ of type object>
 |      T.__new__(S, ...) -> a new object with type S, a subtype of T
Run Code Online (Sandbox Code Playgroud)

在Python中,生成器基本上是一种特定类型的迭代器,它通过使用a yield来从函数返回数据来实现.但是,enumerate实际上是用C实现的,而不是纯Python,所以没有yield涉及.你可以在这里找到源代码:http://hg.python.org/cpython/file/2.7/Objects/enumobject.c

  • @JoshO'Brien有点挑剔:"可迭代"是任何可以迭代的对象,例如list,dict,str,file."迭代器"是实际创建的用于迭代迭代的对象.对于大多数Python容器,您可以通过调用`iter(obj)`来获取它.当你在obj`中执行`for x时,这会隐式发生.编辑:我看到John Y.打败了我到这一步:) (3认同)
  • @JoshO'Brien我不知道有任何方法可以确定给定迭代器返回的数据是懒惰获得还是完全加载到内存中.迭代器只是提供了一种方法,通过暴露`next()`方法,一次(并且只一次)迭代一个对象一次.究竟在`next()`内部发生了什么`调用者不知道. (2认同)

Aar*_*all 10

测试枚举类型:

我将在枚举类型的探索中以及它如何适合Python语言中包含这个重要的测试:

>>> import collections
>>> e = enumerate('abc')
>>> isinstance(e, enumerate)
True
>>> isinstance(e, collections.Iterable)
True
>>> isinstance(e, collections.Iterator)
True
Run Code Online (Sandbox Code Playgroud)

但我们看到:

>>> import types
>>> isinstance(e, types.GeneratorType)
False
Run Code Online (Sandbox Code Playgroud)

所以我们知道枚举对象不是生成器.

来源:

源代码(Python 2.7)中,我们可以看到迭代返回元组的枚举对象(PyEnum_Type),在ABC模块中我们可以看到任何带有a 方法(实际上是属性)的项被定义为迭代器.(在Python 3中)next__iter____next__

标准库测试

所以Abstract Base Class库使用以下测试:

>>> hasattr(e, 'next') and hasattr(e, '__iter__')
True
Run Code Online (Sandbox Code Playgroud)

所以我们知道枚举类型是迭代器.但是我们看到Generator类型是由 文档中的 yield 或生成器表达式的函数创建的.因此生成器是迭代器,因为它们具有next__iter__方法,但并非所有迭代器都必须是生成器,正如我们在此枚举对象中看到的那样.

那么我们知道什么enumerate呢?

从文档和源代码中,我们知道enumerate返回一个枚举对象,我们知道它是一个迭代器,即使我们的测试声明它明确不是一个生成器.

我们还从文档中了解到,生成器类型只是" 提供了一种实现迭代器协议的便捷方式. "因此,生成器是迭代器的子集.此外,这允许我们推导出以下概括:

所有生成器都是迭代器,但并非所有迭代器都是生成器.

因此,虽然我们可以将枚举对象变为生成器:

>>> g = (i for i in e)
>>> isinstance(g, types.GeneratorType)
True
Run Code Online (Sandbox Code Playgroud)

我们不能指望它本身就是一个发电机,所以这将是错误的测试.

那要测试什么?

这意味着你不应该测试一个生成器,你应该使用我提供的第一个测试,而不是重新实现标准库(我希望今天可以免除这个).

如果需要枚举类型,您可能希望允许带有整数索引的元组的迭代或迭代器,并且以下内容将返回True:

isinstance(g, collections.Iterable)
Run Code Online (Sandbox Code Playgroud)

如果您只需要特定的枚举类型:

isinstance(e, enumerate)
Run Code Online (Sandbox Code Playgroud)

PS如果你感兴趣,这里是生成器的源代码实现:http://hg.python.org/cpython/file/785e29d8ce13/Objects/genobject.c