迭代器和生成器之间的区别是什么?举一些例子来说明你在什么时候使用每种情况会很有帮助。
当前回答
生成器函数,生成器对象,生成器:
Generator函数就像Python中的常规函数一样,但它包含一个或多个yield语句。Generator函数是一个很好的工具,可以尽可能简单地创建Iterator对象。generator函数返回的Iterator对象也称为generator对象或generator。
在这个例子中,我创建了一个Generator函数,它返回一个Generator对象< Generator对象fib at 0x01342480>。就像其他迭代器一样,Generator对象可以在for循环中使用,也可以与从Generator返回下一个值的内置函数next()一起使用。
def fib(max):
a, b = 0, 1
for i in range(max):
yield a
a, b = b, a + b
print(fib(10)) #<generator object fib at 0x01342480>
for i in fib(10):
print(i) # 0 1 1 2 3 5 8 13 21 34
print(next(myfib)) #0
print(next(myfib)) #1
print(next(myfib)) #1
print(next(myfib)) #2
因此,生成器函数是创建Iterator对象的最简单方法。
迭代器:
每个生成器对象都是迭代器,反之亦然。如果自定义迭代器对象的类实现了__iter__和__next__方法(也称为迭代器协议),则可以创建自定义迭代器对象。
然而,使用生成器函数来创建迭代器要容易得多,因为它们简化了迭代器的创建,但是自定义迭代器给了你更多的自由,你也可以根据你的需求实现其他方法,如下面的例子所示。
class Fib:
def __init__(self,max):
self.current=0
self.next=1
self.max=max
self.count=0
def __iter__(self):
return self
def __next__(self):
if self.count>self.max:
raise StopIteration
else:
self.current,self.next=self.next,(self.current+self.next)
self.count+=1
return self.next-self.current
def __str__(self):
return "Generator object"
itobj=Fib(4)
print(itobj) #Generator object
for i in Fib(4):
print(i) #0 1 1 2
print(next(itobj)) #0
print(next(itobj)) #1
print(next(itobj)) #1
其他回答
迭代器是使用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
这篇文章涵盖了两者之间的许多细节差异,但想在两者之间的概念差异上添加一些东西:
[…GoF书中定义的迭代器从集合中检索项,而生成器可以“凭空”生成项。这就是为什么斐波那契序列生成器是一个常见的例子:无限级数的数字不能存储在一个集合中。
Ramalho,卢西亚诺。流利的Python(第415页)。O ' reilly媒体。Kindle版。
当然,它并没有涵盖所有的方面,但我认为它给出了一个很好的概念,当一个人是有用的。
迭代器和生成器之间的区别是什么?举一些例子来说明你在什么时候使用每种情况会很有帮助。
总结:迭代器是具有__iter__和__next__ (Python 2中的next)方法的对象。生成器提供了一种简单的内置方法来创建iterator实例。
包含yield的函数仍然是一个函数,当调用它时,返回一个生成器对象的实例:
def a_function():
"when called, returns generator object"
yield
生成器表达式也返回一个生成器:
a_generator = (i for i in range(0))
有关更深入的阐述和示例,请继续阅读。
Generator是一个迭代器
具体来说,generator是迭代器的子类型。
>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True
我们可以通过几种方式创建生成器。一种非常常见和简单的方法是使用函数。
具体来说,包含yield的函数是一个函数,当调用它时,返回一个生成器:
>>> def a_function():
"just a function definition with yield in it"
yield
>>> type(a_function)
<class 'function'>
>>> a_generator = a_function() # when called
>>> type(a_generator) # returns a generator
<class 'generator'>
生成器也是一个迭代器:
>>> isinstance(a_generator, collections.Iterator)
True
迭代器是可迭代对象
迭代器是可迭代对象,
>>> issubclass(collections.Iterator, collections.Iterable)
True
它需要一个返回迭代器的__iter__方法:
>>> collections.Iterable()
Traceback (most recent call last):
File "<pyshell#79>", line 1, in <module>
collections.Iterable()
TypeError: Can't instantiate abstract class Iterable with abstract methods __iter__
一些可迭代对象的例子是内置元组、列表、字典、集合、冻结集、字符串、字节字符串、字节数组、范围和memoryview:
>>> all(isinstance(element, collections.Iterable) for element in (
(), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
迭代器需要一个next或__next__方法
在Python 2中:
>>> collections.Iterator()
Traceback (most recent call last):
File "<pyshell#80>", line 1, in <module>
collections.Iterator()
TypeError: Can't instantiate abstract class Iterator with abstract methods next
在Python 3中:
>>> collections.Iterator()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Iterator with abstract methods __next__
我们可以使用iter函数从内置对象(或自定义对象)中获取迭代器:
>>> all(isinstance(iter(element), collections.Iterator) for element in (
(), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
当你试图使用for循环对象时,__iter__方法会被调用。然后在迭代器对象上调用__next__方法,为循环取出每一项。迭代器在耗尽它时抛出StopIteration,此时它不能被重用。
来自文档
从内置类型文档的迭代器类型部分的生成器类型部分:
Python的生成器提供了一种实现迭代器协议的方便方法。如果容器对象的__iter__()方法被实现为生成器,它将自动返回一个迭代器对象(技术上,一个生成器对象),提供__iter__()和next() [__next__() in python3]方法。关于生成器的更多信息可以在yield表达式的文档中找到。
(强调)。
从这里我们了解到generator是一种(方便的)迭代器类型。
迭代器对象示例
您可以通过创建或扩展自己的对象来创建实现Iterator协议的对象。
class Yes(collections.Iterator):
def __init__(self, stop):
self.x = 0
self.stop = stop
def __iter__(self):
return self
def next(self):
if self.x < self.stop:
self.x += 1
return 'yes'
else:
# Iterators must raise when done, else considered broken
raise StopIteration
__next__ = next # Python 3 compatibility
但是简单地使用Generator更容易做到这一点:
def yes(stop):
for _ in range(stop):
yield 'yes'
或者更简单,生成器表达式(类似于列表推导式):
yes_expr = ('yes' for _ in range(stop))
它们都可以以同样的方式使用:
>>> stop = 4
>>> for i, y1, y2, y3 in zip(range(stop), Yes(stop), yes(stop),
('yes' for _ in range(stop))):
... print('{0}: {1} == {2} == {3}'.format(i, y1, y2, y3))
...
0: yes == yes == yes
1: yes == yes == yes
2: yes == yes == yes
3: yes == yes == yes
结论
当需要将Python对象扩展为可迭代的对象时,可以直接使用Iterator协议。
然而,在绝大多数情况下,您最适合使用yield来定义返回Generator Iterator的函数或考虑Generator expression。
最后,请注意生成器作为协程提供了更多的功能。我在回答“yield”关键字做什么?”时,深入地解释了Generators和yield语句。
之前的回答忽略了这一点:生成器有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有一个更简单的选择:生成器函数。生成器函数不是普通的函数。不是遍历代码并返回最终结果,而是延迟代码,函数立即返回一个生成器对象。
生成器对象类似于迭代器对象,因为它实现了迭代器协议。这对于大多数目的来说已经足够好了。在其他答案中有许多生成器的例子。
简而言之,迭代器是一个对象,它允许您迭代另一个对象,无论它是一个集合还是其他一些值的来源。生成器是一个简化的迭代器,它或多或少完成相同的工作,但更容易实现。
通常情况下,如果你只需要发电机,你会选择发电机。但是,如果您正在构建一个更复杂的对象,其中包含其他特性之间的迭代,则应该使用迭代器协议。
推荐文章
- 证书验证失败:无法获得本地颁发者证书
- 当使用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中获得所有直接子目录