迭代器和生成器之间的区别是什么?举一些例子来说明你在什么时候使用每种情况会很有帮助。
当前回答
这篇文章涵盖了两者之间的许多细节差异,但想在两者之间的概念差异上添加一些东西:
[…GoF书中定义的迭代器从集合中检索项,而生成器可以“凭空”生成项。这就是为什么斐波那契序列生成器是一个常见的例子:无限级数的数字不能存储在一个集合中。
Ramalho,卢西亚诺。流利的Python(第415页)。O ' reilly媒体。Kindle版。
当然,它并没有涵盖所有的方面,但我认为它给出了一个很好的概念,当一个人是有用的。
其他回答
迭代器是使用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
对于相同的数据,你可以比较两种方法:
def myGeneratorList(n):
for i in range(n):
yield i
def myIterableList(n):
ll = n*[None]
for i in range(n):
ll[i] = i
return ll
# Same values
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
for i1, i2 in zip(ll1, ll2):
print("{} {}".format(i1, i2))
# Generator can only be read once
ll1 = myGeneratorList(10)
ll2 = myIterableList(10)
print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))
# Generator can be read several times if converted into iterable
ll1 = list(myGeneratorList(10))
ll2 = myIterableList(10)
print("{} {}".format(len(list(ll1)), len(ll2)))
print("{} {}".format(len(list(ll1)), len(ll2)))
此外,如果检查内存占用,生成器占用的内存要少得多,因为它不需要同时将所有值存储在内存中。
强烈推荐Ned Batchelder的迭代器和生成器示例
一个没有生成器的方法,它对偶数进行处理
def evens(stream):
them = []
for n in stream:
if n % 2 == 0:
them.append(n)
return them
而通过使用发电机
def evens(stream):
for n in stream:
if n % 2 == 0:
yield n
我们不需要任何列表或返回语句 有效的大/无限长的流…它只是走动并产生值
调用evens方法(生成器)和往常一样
num = [...]
for n in evens(num):
do_smth(n)
发电机也用于打破双环
迭代器
满页的书是可迭代对象,书签是可迭代对象 迭代器
而这个书签除了下一步移动什么也做不了
litr = iter([1,2,3])
next(litr) ## 1
next(litr) ## 2
next(litr) ## 3
next(litr) ## StopIteration (Exception) as we got end of the iterator
使用生成器…我们需要一个函数
使用迭代器…我们需要next和iter
如前所述:
Generator函数返回一个迭代器对象
Iterator的全部好处:
每次在内存中存储一个元素
添加一个答案,因为现有的答案都没有专门解决官方文献中的困惑。
生成器函数是用yield而不是return定义的普通函数。当被调用时,生成器函数返回一个生成器对象,这是一种迭代器——它有一个next()方法。当调用next()时,将返回生成器函数产生的下一个值。
函数或对象都可以被称为“生成器”,这取决于你阅读的Python源文档。Python术语表表示生成器函数,而Python wiki表示生成器对象。Python教程成功地在三句话中暗示了这两种用法:
生成器是用于创建迭代器的简单而强大的工具。它们像常规函数一样编写,但在需要返回数据时使用yield语句。每次在它上调用next()时,生成器都会从停止的地方恢复(它会记住所有的数据值和最后执行的语句)。
前两句话用生成器函数标识生成器,而第三句话用生成器对象标识它们。
尽管存在这些困惑,但人们可以从Python语言参考中找到明确的最终答案:
yield表达式仅在定义生成器函数时使用,并且只能在函数定义的主体中使用。在函数定义中使用yield表达式足以导致该定义创建一个生成器函数,而不是普通函数。 当调用generator函数时,它返回一个称为generator的迭代器。然后,该生成器控制生成器函数的执行。
因此,在正式和精确的用法中,“generator”不合格指的是生成器对象,而不是生成器功能。
上面的参考资料是针对Python 2的,但Python 3语言参考资料也说了同样的事情。然而,Python 3术语表指出
发电机……通常指生成器函数,但在某些上下文中也可能指生成器迭代器。在意图不明确的情况下,使用完整的术语可以避免歧义。
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
如果您的应用程序中确实需要这些额外的功能。
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用pip3安装包时,“Python中的ssl模块不可用”
- 无法切换Python与pyenv
- Python if not == vs if !=
- 如何从scikit-learn决策树中提取决策规则?
- 为什么在Mac OS X v10.9 (Mavericks)的终端中apt-get功能不起作用?
- 将旋转的xtick标签与各自的xtick对齐
- 为什么元组可以包含可变项?
- 如何合并字典的字典?
- 如何创建类属性?
- 不区分大小写的“in”
- 在Python中获取迭代器中的元素个数
- 解析日期字符串并更改格式
- 使用try和。Python中的if
- 如何在Python中获得所有直接子目录