Python的生成器和迭代器之间的区别
迭代器和生成器有什么区别? 当你使用每个案例的一些例子将是有帮助的。
iterator
是一个更一般的概念:任何对象的类都有next
方法(Python 3中的__iter__
)和一个return self
的__iter__
方法。
每个生成器都是一个迭代器,但不是相反的。 通过调用具有一个或多个yield
expression式(在Python 2.5和更早版本中为yield
语句)的函数来构build生成器,并且该对象符合上一个iterator
的定义。
当你需要一个具有复杂的状态维护行为的类,或者想要暴露除next
之外的其他方法(以及__iter__
和__init__
)时,你可能需要使用自定义迭代器而不是生成器。 大多数情况下,一个生成器(有时足够简单的需求,一个生成器expression式 )就足够了,而且编码更简单,因为状态维护(在合理范围内)基本上是由框架暂停和恢复完成的。
例如,一个生成器如:
def squares(start, stop): for i in range(start, stop): yield i * i generator = squares(a, b)
或等价的生成器expression式(genexp)
generator = (i*i for i in range(a, b))
将需要更多代码构build为自定义迭代器:
class Squares(object): def __init__(self, start, stop): self.start = start self.stop = stop def __iter__(self): return self def next(self): 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
如果您在应用程序中有任何实际需要这种额外的function。
迭代器和生成器有什么区别? 当你使用每个案例的一些例子将是有帮助的。
总结:迭代器是具有__iter__
和__next__
(Python 2中的next
)方法的对象。 生成器提供了一种简单的内置方法来创build迭代器的实例。
带有yield的函数仍然是一个函数,在调用时返回一个生成器对象的实例:
def a_function(): "when called, returns generator object" yield
生成器expression式也返回一个生成器:
a_generator = (i for i in range(0))
为了更深入的阐述和例子,请继续阅读。
生成器是一个迭代器
具体而言,生成器是迭代器的一个子types。
>>> import collections, types >>> issubclass(types.GeneratorType, collections.Iterator) True
我们可以通过几种方式创build一个生成 一个非常普通和简单的方法是使用一个函数。
具体来说,一个带有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
这需要返回一个Iterator的__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__
iterables的一些例子是元组,列表,集合,字典,string和范围对象:
>>> all(isinstance(element, collections.Iterable) for element in ( (), [], {}, set(), '', range(0))) 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
函数从内build对象(或自定义对象)获取迭代器:
>>> all(isinstance(iter(element), collections.Iterator) for element in ( (), [], {}, set(), '', range(0))) True
__iter__
函数是当你试图用for循环来使用一个对象时被调用的函数。 然后在迭代器对象上调用__next__
或next
来获取每个项目的循环。 迭代器在耗尽之后会引发StopIteration
,并且在此时不能重用。
从文档:
从“内置types” 文档的“迭代器types”部分的“生成器types”部分:
Python的生成器提供了一个实现迭代器协议的简便方法。 如果一个容器对象的
__iter__()
方法被实现为一个生成器,它将自动返回一个在Python 3中提供__iter__()
和next()
[__next__()
__iter__()
方法的迭代器对象(技术上讲,一个生成器对象)。 有关生成器的更多信息可以在yieldexpression式的文档中find。
(强调补充说。)
所以从这里我们知道生成器是一个(方便的)types的迭代器。
示例迭代器对象
您可以通过创build或扩展自己的对象来创build实现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'
或者也许更简单,一个生成器expression式(类似于列表parsing):
yes_expr = ('yes' for _ in range(stop))
它们都可以用同样的方式使用:
>>> stop = 4 >>> for i, ys in enumerate(zip(Yes(stop), yes(stop), ('yes' for _ in range(stop))): >>> 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的函数,或者考虑使用Generator Expressions。
最后,请注意,生成器提供了更多的协同function。 我在“我的收益”关键字的作用是什么?
迭代器:
迭代器是使用next()
方法获取序列的下一个值的对象。
发电机:
生成器是使用yield
方法生成或生成一系列值的函数。
每个next()
方法调用generator函数返回的generator对象(例如下例中的ex: f
)(例如下例中的ex: foo()
函数),依次生成下一个值。
当一个生成器函数被调用时,它将返回一个生成器对象,而不会开始执行该函数。 当第一次调用next()
方法时,函数开始执行,直到达到yield语句,返回yield值。 产量跟踪logging最后的执行。 第二个next()
调用从以前的值继续。
以下示例演示了yield和调用发生器对象的下一个方法之间的相互作用。
>>> def foo(): ... print "begin" ... for i in range(3): ... print "before yield", i ... yield i ... print "after yield", i ... print "end" ... >>> f = foo() >>> f.next() begin before yield 0 # Control is in for loop 0 >>> f.next() after yield 0 before yield 1 # Continue for loop 1 >>> f.next() after yield 1 before yield 2 2 >>> f.next() after yield 2 end Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>>
添加一个答案,因为没有任何现有的答案专门处理官方文献中的混乱。
Generator函数是使用yield
而不是return
定义的普通函数。 当被调用时,一个生成器函数返回一个生成器对象 ,这是一种迭代器 – 它有一个next()
方法。 当你调用next()
,返回由生成器函数产生的下一个值。
函数或对象可以被称为“生成器”,具体取决于您读取的Python源文档。 Python的词汇表说生成器函数,而Python的wiki意味着生成器对象。 Python教程显着地设法暗示这两个用法在三个句子的空间:
生成器是创build迭代器的简单而强大的工具。 它们像常规函数一样编写,但是只要他们想返回数据就使用yield语句。 每当next()被调用时,生成器会从停止的地方恢复(它会记住所有的数据值以及上次执行的语句)。
前两个句子用发电机function标识发电机,而第三个句子用发电机对象标识它们。
尽pipe所有这些困惑,人们可以找出明确的最后一句话的Python语言参考 :
yieldexpression式仅在定义生成器函数时使用,并且只能在函数定义的主体中使用。 在函数定义中使用yieldexpression式足以导致该定义创build生成器函数,而不是正常函数。
当调用生成器函数时,它将返回一个称为生成器的迭代器。 该生成器然后控制生成器函数的执行。
因此,在forms和精确的使用中, “发电机”不合格是指发电机对象,而不是发电机function。
上面的引用是针对Python 2的,但是Python 3的语言引用也是一样的。 但是, Python 3术语表明
生成器 …通常是指生成器函数,但在某些上下文中可能指的是生成器迭代器。 在意图不明确的情况下,使用完整的术语避免含糊不清。
发电机function,发电机对象,发电机:
Generator函数就像Python中的常规函数,但它包含一个或多个yield
语句。 生成器函数是创buildIterator对象的一个很好的工具。 由生成器函数returend的Iterator对象也被称为Generator对象或Generator 。
在这个例子中,我创build了一个Generator函数,它返回一个Generator对象<generator object fib at 0x01342480>
。 就像其他的迭代器一样,Generator对象可以在for
循环中使用,或者使用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
所以一个生成器函数是创build一个Iterator对象的最简单的方法。
迭代器 :
每个生成器对象都是一个迭代器,但反之亦然。 如果自定义迭代器对象的类实现了__iter__
和__next__
方法(也称为迭代器协议),则可以创build自定义迭代器对象。
然而,使用生成器函数来创build迭代器要容易得多,因为它们简化了它们的创build,但是一个自定义的迭代器给了你更多的自由,你也可以根据你的需求实现其他方法,如下面的例子所示。
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
每个人都有一个非常好的和详细的答案与例子,我真的很感激它。 我只是想给那些概念上还不太清楚的人提供一些简短的答案:
如果你创build你自己的迭代器,那么它有一点涉及 – 你必须创build一个类,至less实现iter和下一个方法。 但是,如果你不想经历这个麻烦,并想快速创build一个迭代器。 幸运的是,Python提供了定义迭代器的捷径。 所有你需要做的就是定义一个至less有一个调用的函数来产生,现在当你调用这个函数的时候,它会返回一些类似迭代器的东西(你可以调用下一个方法并在for循环中使用它)。 这个东西在Python中有一个叫做Generator的名字
希望澄清一下。
您可以比较两种方法的相同数据:
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)))
此外,如果您检查内存占用量,生成器将占用更less的内存,因为它不需要同时将所有值存储在内存中。