迭代器和生成器之间的区别是什么?举一些例子来说明你在什么时候使用每种情况会很有帮助。
当前回答
如果没有另外两个概念:可迭代对象和迭代器协议,就很难回答这个问题。
What is difference between iterator and iterable? Conceptually you iterate over iterable with the help of corresponding iterator. There are a few differences that can help to distinguish iterator and iterable in practice: One difference is that iterator has __next__ method, iterable does not. Another difference - both of them contain __iter__ method. In case of iterable it returns the corresponding iterator. In case of iterator it returns itself. This can help to distinguish iterator and iterable in practice.
>>> x = [1, 2, 3]
>>> dir(x)
[... __iter__ ...]
>>> x_iter = iter(x)
>>> dir(x_iter)
[... __iter__ ... __next__ ...]
>>> type(x_iter)
list_iterator
What are iterables in python? list, string, range etc. What are iterators? enumerate, zip, reversed etc. We may check this using the approach above. It's kind of confusing. Probably it would be easier if we have only one type. Is there any difference between range and zip? One of the reasons to do this - range has a lot of additional functionality - we may index it or check if it contains some number etc. (see details here). How can we create an iterator ourselves? Theoretically we may implement Iterator Protocol (see here). We need to write __next__ and __iter__ methods and raise StopIteration exception and so on (see Alex Martelli's answer for an example and possible motivation, see also here). But in practice we use generators. It seems to be by far the main method to create iterators in python.
我可以给你一些更有趣的例子,展示这些概念在实践中的一些令人困惑的用法:
in keras we have tf.keras.preprocessing.image.ImageDataGenerator; this class doesn't have __next__ and __iter__ methods; so it's not an iterator (or generator); if you call its flow_from_dataframe() method you'll get DataFrameIterator that has those methods; but it doesn't implement StopIteration (which is not common in build-in iterators in python); in documentation we may read that "A DataFrameIterator yielding tuples of (x, y)" - again confusing usage of terminology; we also have Sequence class in keras and that's custom implementation of a generator functionality (regular generators are not suitable for multithreading) but it doesn't implement __next__ and __iter__, rather it's a wrapper around generators (it uses yield statement);
其他回答
每个人都有一个非常漂亮和冗长的答案,我真的很感激。我只是想给那些在概念上还不太清楚的人一个简短的回答:
If you create your own iterator, it is a little bit involved - you have to create a class and at least implement the iter and the next methods. But what if you don't want to go through this hassle and want to quickly create an iterator. Fortunately, Python provides a short-cut way to defining an iterator. All you need to do is define a function with at least 1 call to yield and now when you call that function it will return "something" which will act like an iterator (you can call next method and use it in a for loop). This something has a name in Python called Generator
希望这能澄清一点。
迭代器是使用next()方法获取序列的以下值的对象。
生成器是使用yield关键字生成或生成值序列的函数。
由生成器函数(下面的ex: foo())返回的生成器对象(下面的ex: f)上的每个next()方法调用,都会生成序列中的下一个值。
当调用生成器函数时,它返回一个生成器对象,甚至不需要开始执行该函数。当第一次调用next()方法时,函数开始执行,直到到达yield语句,该语句返回yield值。收益率会跟踪发生了什么,也就是说,它会记住最后一次执行。其次,next()调用从前一个值开始。
下面的示例演示生成器对象上yield和对next方法的调用之间的相互作用。
>>> def foo():
... print("begin")
... for i in range(3):
... print("before yield", i)
... yield i
... print("after yield", i)
... print("end")
...
>>> f = foo()
>>> next(f)
begin
before yield 0 # Control is in for loop
0
>>> next(f)
after yield 0
before yield 1 # Continue for loop
1
>>> next(f)
after yield 1
before yield 2
2
>>> next(f)
after yield 2
end
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
之前的回答忽略了这一点:生成器有close方法,而典型的迭代器没有。close方法在生成器中触发StopIteration异常,该异常可能在迭代器中的finally子句中被捕获,以获得运行一些清理的机会。这种抽象使得它在大型迭代器中比简单迭代器更有用。可以像关闭文件一样关闭生成器,而不必担心下面有什么。
也就是说,我个人对第一个问题的回答是:iteratable只有__iter__方法,典型的迭代器只有__next__方法,生成器既有__iter__又有__next__,还有一个附加的close。
For the second question, my personal answer would be: in a public interface, I tend to favor generators a lot, since it’s more resilient: the close method an a greater composability with yield from. Locally, I may use iterators, but only if it’s a flat and simple structure (iterators does not compose easily) and if there are reasons to believe the sequence is rather short especially if it may be stopped before it reach the end. I tend to look at iterators as a low level primitive, except as literals.
对于控制流而言,生成器是一个与承诺同样重要的概念:两者都是抽象的和可组合的。
可迭代对象是可以(自然地)迭代的对象。然而,要做到这一点,你将需要一个类似迭代器对象的东西,是的,术语可能令人困惑。可迭代对象包括__iter__方法,该方法将返回可迭代对象的迭代器对象。
迭代器对象是一个实现迭代器协议的对象——一组规则。在这种情况下,它必须至少有这两个方法:__iter__和__next__。__next__方法是一个提供新值的函数。__iter__方法返回迭代器对象。在更复杂的对象中,可能有单独的迭代器,但在更简单的情况下,__iter__返回对象本身(通常返回self)。
一个iterable对象是一个列表对象。它不是一个迭代器,但它有一个__iter__方法,返回一个迭代器。你可以直接以things.__iter__()的形式调用这个方法,或者使用iter(things)。
如果你想遍历任何集合,你需要使用它的迭代器:
things_iterator = iter(things)
for i in things_iterator:
print(i)
然而,Python会自动使用迭代器,这就是为什么你从来没有看到上面的例子。相反,你可以这样写:
for i in things:
print(i)
自己编写迭代器可能很乏味,所以Python有一个更简单的选择:生成器函数。生成器函数不是普通的函数。不是遍历代码并返回最终结果,而是延迟代码,函数立即返回一个生成器对象。
生成器对象类似于迭代器对象,因为它实现了迭代器协议。这对于大多数目的来说已经足够好了。在其他答案中有许多生成器的例子。
简而言之,迭代器是一个对象,它允许您迭代另一个对象,无论它是一个集合还是其他一些值的来源。生成器是一个简化的迭代器,它或多或少完成相同的工作,但更容易实现。
通常情况下,如果你只需要发电机,你会选择发电机。但是,如果您正在构建一个更复杂的对象,其中包含其他特性之间的迭代,则应该使用迭代器协议。
iterator是一个更通用的概念:任何具有__next__方法(Python 2中的next)和__iter__方法且返回self的对象。
每个生成器都是迭代器,反之亦然。生成器是通过调用具有一个或多个yield表达式(yield语句,在Python 2.5及更早版本中)的函数来构建的,它是一个满足上一段对迭代器定义的对象。
当你需要一个具有复杂状态维护行为的类,或者想公开__next__(以及__iter__和__init__)之外的其他方法时,你可能想使用自定义迭代器,而不是生成器。大多数情况下,一个生成器(有时,对于足够简单的需求,一个生成器表达式)就足够了,而且编码更简单,因为状态维护(在合理的范围内)基本上是由框架挂起和恢复“为您完成”的。
例如,一个生成器,如:
def squares(start, stop):
for i in range(start, stop):
yield i * i
generator = squares(a, b)
或等效的生成器表达式(genexp)
generator = (i*i for i in range(a, b))
将需要更多的代码来构建自定义迭代器:
class Squares(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self): return self
def __next__(self): # next in Python 2
if self.start >= self.stop:
raise StopIteration
current = self.start * self.start
self.start += 1
return current
iterator = Squares(a, b)
但是,当然,使用类Squares,你可以很容易地提供额外的方法。
def current(self):
return self.start
如果您的应用程序中确实需要这些额外的功能。
推荐文章
- 使用python创建一个简单的XML文件
- APT命令行界面式的yes/no输入?
- 如何打印出状态栏和百分比?
- 在Python中获取大文件的MD5哈希值
- 在Python格式字符串中%s是什么意思?
- 如何循环通过所有但最后一项的列表?
- python用什么方法避免默认参数为空列表?
- ValueError: numpy。Ndarray大小改变,可能表示二进制不兼容。期望从C头得到88,从PyObject得到80
- Anaconda /conda -安装特定的软件包版本
- 我在哪里调用Keras的BatchNormalization函数?
- 打印测试执行时间并使用py.test锁定缓慢的测试
- 插入一行到熊猫数据框架
- 要列出Pandas DataFrame列
- 在Django模型中存储电话号码的最佳方法是什么?
- 从导入的模块中模拟函数