Does enumerate() produce a generator object?
作为一个完整的python新手,它看起来的确如此。运行以下…
1 2 3 4 5 6 7 8 9 | x = enumerate(['fee', 'fie', 'foe']) x.next() # Out[1]: (0, 'fee') list(x) # Out[2]: [(1, 'fie'), (2, 'foe')] list(x) # Out[3]: [] |
…我注意到:(a)
另一方面,这两个最乐观的答案问题关于如何确定对象是否是生成器表示
1 2 3 4 5 6 7 8 9 10 | import types import inspect x = enumerate(['fee', 'fie', 'foe']) isinstance(x, types.GeneratorType) # Out[4]: False inspect.isgenerator(x) # Out[5]: False |
号
…对这个问题的第三个糟糕的上票回答似乎表明,
1 2 3 4 5 | def isgenerator(iterable): return hasattr(iterable,'__iter__') and not hasattr(iterable,'__len__') isgenerator(x) # Out[8]: True |
怎么了?
而不是继续写下通过我的头,我就把这个扔给你们这些人,他们会马上知道答案。
虽然python文档说
1 2 3 4 5 | def enumerate(sequence, start=0): n = start for elem in sequence: yield n, elem n += 1 |
real
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | >>> 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中,生成器基本上是一种特定类型的迭代器,通过使用
枚举类型测试:
我将在探索枚举类型以及它如何适合于Python语言的过程中包括这个重要的测试:
1 2 3 4 5 6 7 8 | >>> import collections >>> e = enumerate('abc') >>> isinstance(e, enumerate) True >>> isinstance(e, collections.Iterable) True >>> isinstance(e, collections.Iterator) True |
但我们看到:
1 2 3 | >>> import types >>> isinstance(e, types.GeneratorType) False |
号
所以我们知道枚举对象不是生成器。
资料来源:在源代码(python 2.7)中,我们可以看到迭代返回元组的枚举对象(pyenum-type),在abc模块中,我们可以看到具有
因此抽象基类库使用以下测试:
1 2 | >>> hasattr(e, 'next') and hasattr(e, '__iter__') True |
所以我们知道枚举类型是迭代器。但是我们看到生成器类型是由文档或生成器表达式中具有yield的函数创建的。所以生成器是迭代器,因为它们有
从文档和源代码中,我们知道enumerate返回一个enumerate对象,并且通过定义我们知道它是一个迭代器,即使我们的测试声明它显式地不是一个生成器。
我们还从文档中了解到,生成器类型只是"提供了实现迭代器协议的方便方法"。因此,生成器是迭代器的一个子集。此外,这允许我们得出以下概括:
All generators are iterators, but not all iterators are generators.
号
因此,当我们可以使枚举对象成为生成器时:
1 2 3 | >>> g = (i for i in e) >>> isinstance(g, types.GeneratorType) True |
。
我们不能指望它本身就是一个发电机,所以这是一个错误的测试。
那么测试什么呢?这意味着你不应该测试一个生成器,你应该使用我提供的第一个测试,而不是重新实现标准库(我希望我今天可以不做这件事):
如果需要枚举类型,则可能需要允许具有整数索引的元组的iterables或迭代器,并且下面将返回
1 | isinstance(g, collections.Iterable) |
如果只需要一个枚举类型:
1 | isinstance(e, enumerate) |
。
ps如果您感兴趣,这里是generators的源代码实现:http://hg.python.org/cpython/file/785e29d8ce13/objects/genobject.c
Is it in some sense"generator-like", but not an actual generator?
号
是的,是的。你不应该真的在乎它是不是一只鸭子,但只要它走路、说话、闻起来像鸭子。它就像一台发电机,不应该有什么区别。
当您想要扩展功能时,通常使用类似于生成器的类型而不是实际的生成器。例如,
您可以尝试一些事情来证明它既不是生成器也不是生成器的子类:
1 2 3 4 5 6 | >>> x = enumerate(["a","b","c"]) >>> type(x) <type 'enumerate'> >>> import types >>> issubclass(type(x), types.GeneratorType) False |
正如丹尼尔指出的,这是它自己的类型,
因此,它们实现了一些相同的方法,因为它们都是不可重复的。就像列表和生成器都是不可重复的,但它们并不相同。
因此,与其说
希望有帮助!