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

时间:2014-05-14 19:14:44

标签: 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]: []

...我注意到:(a)x确实有next方法,似乎是 生成器需要,(b)x只能迭代一次,a this famous python-tag answer中强调了生成器的特征。

另一方面,this question的两个最高评价答案 关于如何确定一个物体是否是一个发电机似乎 表示enumerate() 返回生成器。

import types
import inspect

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

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

inspect.isgenerator(x)
# Out[5]: False

...虽然该问题的第三个poorly-upvoted answer似乎表明enumerate() 确实实际上返回了一个生成器:

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

isgenerator(x)
# Out[8]: True

那是怎么回事? x是否是发电机?是在某种意义上吗? “类似发电机”,但不是真正的发电机? Python是否使用 duck-typing意味着在上面的最终代码块中概述了测试 实际上是最好的一个?

而不是继续写下贯穿我的可能性 我要把它扔给那些马上就会发给你们的人 知道答案。

5 个答案:

答案 0 :(得分:33)

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

def enumerate(sequence, start=0):
    n = start
    for elem in sequence:
        yield n, elem
        n += 1

真实的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

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

答案 1 :(得分:10)

测试枚举类型:

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

>>> import collections
>>> e = enumerate('abc')
>>> isinstance(e, enumerate)
True
>>> isinstance(e, collections.Iterable)
True
>>> isinstance(e, collections.Iterator)
True

但我们看到了:

>>> import types
>>> isinstance(e, types.GeneratorType)
False

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

来源:

source (of Python 2.7),中,我们可以看到迭代返回PyEnum_Type的枚举对象(the tuple),以及ABC module we can see that any item with a next and __iter__ method (actually, attribute) is defined to be an iterator.__next__ in Python 3.

标准库测试

因此,抽象基类库使用以下测试:

>>> hasattr(e, 'next') and hasattr(e, '__iter__')
True

所以我们知道枚举类型是迭代器。 But we see that a Generator type is created by a function with yield in the documentation or a generator expression。因此,生成器是迭代器,因为它们具有next__iter__方法,但并非所有迭代器都必须是生成器,正如我们在此枚举对象中看到的那样。

那么我们对enumerate了解了什么?

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

我们还从文档中知道生成器类型只是“provide a convenient way to implement the iterator protocol.”因此,生成器是迭代器的子集。此外,这使我们可以推导出以下概括:

  

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

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

>>> g = (i for i in e)
>>> isinstance(g, types.GeneratorType)
True

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

那么测试什么?

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

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

isinstance(g, collections.Iterable)

如果您只想要一个特定的枚举类型:

isinstance(e, enumerate)

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

答案 2 :(得分:4)

  

它在某种意义上是“类似发电机”,而不是真正的发电机吗?

是的,确实如此。如果 是一只鸭子,你应该不在乎它,但只有当它走路,说话,闻起来像是一只鸭子时。它也一个发电机,不应该有真正的区别。

当您想要扩展功能时,通常使用类似生成器的类型而不是实际的生成器。例如。 range也类似于生成器,但它也支持y in range(x)len(range(x))(python2.x中的xrange)之类的内容。

答案 3 :(得分:3)

你可以尝试一些事情向自己证明它既不是生成器也不是生成器的子类:

>>> x = enumerate(["a","b","c"])
>>> type(x)
<type 'enumerate'>
>>> import types
>>> issubclass(type(x), types.GeneratorType)
False

丹尼尔指出,这是它自己的类型,enumerate。这种类型恰好是可迭代的。生成器也是可迭代的。第二个,你提到的低估的答案基本上只是通过谈论__iter__方法来间接指出。

因此,他们可以通过迭代实现一些相同的方法。就像列表和生成器都是可迭代的,但不是一回事。

因此,不要说enumerate类型的某些东西是“类似于生成器”的,而只是简单地说enumerateGeneratorType类都是可迭代的(更进一步)清单等)。 他们如何迭代数据(以及他们存储的数据的形状)可能完全不同,但界面是相同的。

希望有所帮助!

答案 4 :(得分:2)

enumerate生成一个枚举对象。它是一个迭代器,就像一个发生器。