Python的生成器和迭代器之间的区别

new*_*ing 482 python iterator generator

迭代器和生成器之间有什么区别?关于何时使用每种情况的一些示例会很有帮助.

Ale*_*lli 493

iterator是一个更通用的概念:任何类具有next方法的对象(__next__在Python 3中)和一个__iter__方法return self.

每个生成器都是迭代器,但反之亦然.通过调用具有一个或多个yield表达式的函数(yield在Python 2.5及更早版本中的语句)构建生成器,并且该生成器是满足前一段的定义的对象iterator.

当您需要一个具有某种复杂的状态维护行为的类,或者想要公开除next(__iter____init__)之外的其他方法时,您可能希望使用自定义迭代器而不是生成器.大多数情况下,生成器(有时,对于足够简单的需求,生成器表达式)就足够了,并且编码更简单,因为状态维护(在合理的限制范围内)基本上是"为你完成",因为帧被暂停和恢复.

例如,一个生成器,例如:

def squares(start, stop):
    for i in range(start, stop):
        yield i * i

generator = squares(a, b)
Run Code Online (Sandbox Code Playgroud)

或等效的生成器表达式(genexp)

generator = (i*i for i in range(a, b))
Run Code Online (Sandbox Code Playgroud)

将需要更多代码构建为自定义迭代器:

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 3
       if self.start >= self.stop:
           raise StopIteration
       current = self.start * self.start
       self.start += 1
       return current

iterator = Squares(a, b)
Run Code Online (Sandbox Code Playgroud)

但是,当然,通过课程,Squares您可以轻松提供额外的方法,即

    def current(self):
       return self.start
Run Code Online (Sandbox Code Playgroud)

如果您在应用程序中确实需要此类额外功能.

  • 更有趣的是,当类型提示仅产生值的生成器时,可以将其注释为[更简单的迭代器](https://docs.python.org/3/library/typing.html#typing.Generator)(即: `生成器[int, None, None]` === `迭代器[int]`) (3认同)
  • @braaterAfrikaaner 好问题。您可能听说过术语“惰性”(按需)与“急切”(立即)评估。生成器是“懒惰的”。它们实际上是我们创建的子程序。(因此“生成器函数”创建一个子程序,即“生成器”)在上面的示例中,“squares(a,b)”是一个“生成器函数”,如果运行它,它会输出*不是*一系列平方数,而是一个可以随意调用来生成这些数字的“生成器”。(当您想要控制处理和内存分配时,这非常有用。) (2认同)

Aar*_*all 118

迭代器和生成器之间有什么区别?关于何时使用每种情况的一些示例会很有帮助.

总结:迭代器是具有__iter____next__(next在Python 2中)方法的对象.生成器提供了一种简单的内置方法来创建迭代器实例.

具有yield的函数仍然是一个函数,当被调用时,它返回一个生成器对象的实例:

def a_function():
    "when called, returns generator object"
    yield
Run Code Online (Sandbox Code Playgroud)

生成器表达式还返回一个生成器:

a_generator = (i for i in range(0))
Run Code Online (Sandbox Code Playgroud)

有关更深入的说明和示例,请继续阅读.

Generator 迭代器

具体来说,generator是迭代器的子类型.

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True
Run Code Online (Sandbox Code Playgroud)

我们可以通过几种方式创建发电机.一种非常常见且简单的方法是使用函数.

具体来说,一个带有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'>
Run Code Online (Sandbox Code Playgroud)

而且,生成器也是迭代器:

>>> isinstance(a_generator, collections.Iterator)
True
Run Code Online (Sandbox Code Playgroud)

迭代器一个Iterable

迭代器是一个Iterable,

>>> issubclass(collections.Iterator, collections.Iterable)
True
Run Code Online (Sandbox Code Playgroud)

这需要一个__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__
Run Code Online (Sandbox Code Playgroud)

可迭代的一些示例是内置元组,列表,字典,集合,冻结集,字符串,字节字符串,字节数组,范围和内存视图:

>>> all(isinstance(element, collections.Iterable) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
Run Code Online (Sandbox Code Playgroud)

迭代器需要一个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
Run Code Online (Sandbox Code Playgroud)

在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__
Run Code Online (Sandbox Code Playgroud)

我们可以使用函数从内置对象(或自定义对象)获取迭代器iter:

>>> all(isinstance(iter(element), collections.Iterator) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
Run Code Online (Sandbox Code Playgroud)

__iter__当您尝试使用带有for循环的对象时,将调用此方法.然后在__next__迭代器对象上调用该方法,以获取循环的每个项目.迭代器StopIteration在你耗尽它时会引发,并且在那时它不能被重用.

从文档中

从内置类型文档的"迭代器类型"部分的"生成器类型"部分:

Python的生成器提供了一种实现迭代器协议的便捷方法.如果容器对象的__iter__()方法被实现为生成器,它将自动返回提供__iter__()next()[ __next__()在Python 3]方法中的迭代器对象(技术上,生成器对象).有关生成器的更多信息可以在yield表达式的文档中找到.

(重点补充.)

因此,我们从中了解到Generators是一种(方便的)Iterator类型.

示例迭代器对象

您可以通过创建或扩展自己的对象来创建实现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
Run Code Online (Sandbox Code Playgroud)

但是简单地使用Generator来执行此操作更容易:

def yes(stop):
    for _ in range(stop):
        yield 'yes'
Run Code Online (Sandbox Code Playgroud)

或者更简单,一个Generator Expression(与列表推导类似):

yes_expr = ('yes' for _ in range(stop))
Run Code Online (Sandbox Code Playgroud)

它们都可以以相同的方式使用:

>>> 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
Run Code Online (Sandbox Code Playgroud)

结论

当您需要将Python对象扩展为可以迭代的对象时,可以直接使用Iterator协议.

但是,在绝大多数情况下,您最适合yield用于定义返回Generator Iterator或考虑Generator Expressions的函数.

最后,请注意,生成器提供了更多功能作为协同程序.我解释了Generators,以及yield声明,深入解释了我对"yield"关键字"有什么作用?"的回答.


小智 33

迭代器:

迭代器是使用next()方法获取序列的下一个值的对象.

发电机:

生成器是使用yield方法生成或生成值序列的函数.

生成器函数返回的生成next()器对象上的每个方法调用(例如f,如下例所示)(例如foo()下面的例子中的函数),按顺序生成下一个值.

当调用生成器函数时,它返回一个生成器对象,甚至没有开始执行该函数.当next()第一次调用方法时,函数开始执行,直到达到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()
>>> 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
>>>
Run Code Online (Sandbox Code Playgroud)

  • 只是FYI收益率不是方法,它是关键字 (3认同)

Pau*_*aul 20

添加答案是因为现有答案都没有具体解决官方文献中的混淆问题.

生成器函数是使用yield而不是定义的普通函数return.调用时,生成器函数返回一个生成器对象,它是一种迭代器 - 它有一个next()方法.当您调用时next(),将返回由生成器函数生成的下一个值.

根据您阅读的Python源文档,函数或对象可以称为"生成器".在Python的词汇说发生器功能,而Python的维基意味着发电机对象.在Python的教程非常设法暗示用三句话的空间用法:

生成器是一个用于创建迭代器的简单而强大的工具.它们像常规函数一样编写,但只要想要返回数据就使用yield语句.每次调用next()时,生成器就会从它停止的地方恢复(它会记住所有数据值以及上次执行的语句).

前两个句子用生成器函数标识生成器,而第三个句子用生成器对象标识它们.

尽管存在这些混淆,但人们可以找到Python语言参考,以获得清晰和最终的单词:

yield表达式仅在定义生成器函数时使用,并且只能在函数定义的主体中使用.在函数定义中使用yield表达式足以使该定义创建生成器函数而不是正常函数.

当调用生成器函数时,它返回一个称为生成器的迭代器.然后该生成器控制生成器函数的执行.

因此,在正式和精确的使用中,"发电机"不合格意味着发电机对象,而不是发电机功能.

以上参考资料适用于Python 2,但Python 3语言参考说明了相同的内容.但是,Python 3术语表指出了这一点

generator ...通常指生成器函数,但在某些上下文中可能引用生成器迭代器.如果预期含义不明确,则使用完整术语可避免含糊不清.

  • 1.无论为什么不应该混淆的理论原因,对这个问题的其他答案的评论在没有解决的情况下否认和相互矛盾,表明存在实际的混淆.2.偶然的不精确是好的,但精确,权威的来源至少应该是SO的选择之一.我在当前项目中广泛使用生成器函数和对象,在设计和编码时区别非常重要.很高兴知道现在使用什么术语,所以我不必在以后更改几十个变量名和注释. (5认同)
  • @Paul,感谢您写下这个答案。这种混淆很重要,因为生成器对象和生成器函数之间的区别在于获得所需行为和必须查找生成器之间的区别。 (4认同)
  • 想象一下数学文献,其中功能与其返回值之间没有区别.偶尔将它们非正式地混合起来很方便,但它增加了各种错误的风险.如果区别在常规,语言和符号中没有形式化,那么先进的现代数学将会受到严重和不必要的阻碍. (2认同)
  • 绕过发电机或发电机功能的高阶函数可能听起来很奇怪,但对我来说它们已经出现了.我在Apache Spark工作,它强制执行一个非常实用的编程风格.这些函数必须创建,传入和传递各种对象才能完成任务.我遇到过一些情况,我忘记了我正在使用什么样的"发电机".变量名称和注释中的提示使用一致和正确的术语,有助于消除混淆.一个Pythonist的默默无闻可以成为另一个项目设计的中心! (2认同)

jou*_*ell 14

无代码 4 行备忘单:

A generator function is a function with yield in it.

A generator expression is like a list comprehension. It uses "()" vs "[]"

A generator object (often called 'a generator') is returned by both above.

A generator is also a subtype of iterator.
Run Code Online (Sandbox Code Playgroud)


Mar*_*afa 12

Examples from Ned Batchelder highly recommended for iterators and generators

A method without generators that do something to even numbers

def evens(stream):
   them = []
   for n in stream:
      if n % 2 == 0:
         them.append(n)
   return them
Run Code Online (Sandbox Code Playgroud)

while by using a generator

def evens(stream):
    for n in stream:
        if n % 2 == 0:
            yield n
Run Code Online (Sandbox Code Playgroud)
  • We don't need any list nor a return statement
  • Efficient for large/ infinite length stream ... it just walks and yield the value

Calling the evens method (generator) is as usual

num = [...]
for n in evens(num):
   do_smth(n)
Run Code Online (Sandbox Code Playgroud)
  • Generator also used to Break double loop

Iterator

A book full of pages is an iterable, A bookmark is an iterator

and this bookmark has nothing to do except to move next

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
Run Code Online (Sandbox Code Playgroud)

To use Generator ... we need a function

To use Iterator ... we need next and iter

As been said:

A Generator function returns an iterator object

The Whole benefit of Iterator:

Store one element a time in memory


Hea*_*ify 9

每个人都有一个非常好的和冗长的答案与例子,我真的很感激.我只想给那些在概念上还不太清楚的人提供简短的几行答案:

如果你创建自己的迭代器,它有点涉及 - 你必须创建一个类,至少实现iter和下一个方法.但是,如果你不想经历这个麻烦并希望快速创建一个迭代器,该怎么办呢?幸运的是,Python提供了一种定义迭代器的简便方法.你需要做的就是定义一个函数,至少有1次调用yield,现在当你调用该函数时,它将返回" 某事物 ",它将像迭代器一样(你可以调用next方法并在for循环中使用它).这个东西在Python中有一个叫做Generator的名字

希望澄清一下.


pio*_*ski 9

该线程详细介绍了两者之间的所有差异,但想在两者之间的概念差异上添加一些内容:

\n
\n

[...] GoF 书中定义的迭代器从集合中检索项目,而生成器可以产生项目 \xe2\x80\x9cout of Thin Air\xe2\x80\x9d。这就是斐波那契数列生成器是一个常见示例的原因:无限系列的数字无法存储在集合中。

\n
\n

拉马略、卢西亚诺。流利的 Python(第 415 页)。奥莱利媒体。Kindle版。

\n

当然,它并没有涵盖所有方面,但我认为它提供了一个有用的概念。

\n


Hib*_*u57 8

先前的答案未添加此功能:生成器具有close方法,而典型的迭代器则没有。该close方法StopIteration在生成器中触发一个异常,该异常可能会finally在该迭代器的子句中捕获,从而有机会运行一些清理操作。这种抽象使它比简单的迭代器在大型迭代器中最有用。一个人可以关闭一个生成器,就像一个人可以关闭一个文件一样,而不必担心底层内容。

也就是说,我对第一个问题的个人回答是:iteratable __iter__仅具有一个方法,典型的迭代器__next__仅具有一个方法,生成器具有an __iter__和a __next__以及一个extra close

对于第二个问题,我个人的回答是:在公共界面中,我倾向于偏爱生成器,因为它更具弹性:该close方法具有更大的可组合性yield from。在本地,我可以使用迭代器,但前提是它是一个平面且简单的结构(迭代器不容易编写),并且有理由相信该序列很短,尤其是在序列结束之前可能已停止的情况。我倾向于将迭代器视为低级原语,而不是文字。

对于控制流而言,生成器是一个与承诺一样重要的概念:两者都是抽象的且可组合的。

  • 另一个答案(/sf/answers/1984721091/)指出“迭代器是可迭代的”。既然迭代器有一个 `__iter__` 方法,为什么迭代器只能有 `__next__`?如果它们应该是可迭代的,我希望它们也必须具有`__iter__`。 (2认同)
  • @bli:AFAICS 这里的答案[指的是标准 PEP234](https://www.python.org/dev/peps/pep-0234/),所以它是正确的,而另一个答案指的是一些实现,所以这是值得怀疑的。该标准只需要可迭代对象上的 __iter__` 来返回迭代器,而迭代器只需要一个 `next` 方法(Python3 中的 `__next__` )。请不要将标准(用于鸭子类型)与其实现(特定的 Python 解释器如何实现它)混淆。这有点像生成器函数(定义)和生成器对象(实现)之间的混淆。;) (2认同)

N R*_*awa 6

发电机功能,发电机对象,发电机:

一个发电机的功能就像Python中的常规功能,但它包含一个或多个yield语句.生成器函数是创建Iterator对象的一个很好的工具 .生成器函数的Iterator对象returend也称为Generator对象Generator.

在这个例子中,我创建了一个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
Run Code Online (Sandbox Code Playgroud)

因此,生成器函数是创建Iterator对象的最简单方法.

迭代器:

每个生成器对象都是迭代器,但反之亦然.如果它的类实现__iter____next__方法(也称为迭代器协议),则可以创建自定义迭代器对象 .

但是,使用generator函数创建迭代器要容易得多,因为它们可以简化创建过程,但是自定义迭代器可以为您提供更多自由,您还可以根据需要实现其他方法,如下例所示.

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
Run Code Online (Sandbox Code Playgroud)


tas*_*hka 5

您可以比较相同数据的两种方法:

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)))
Run Code Online (Sandbox Code Playgroud)

此外,如果您检查内存占用,生成器占用的内存要少得多,因为它不需要同时将所有值存储在内存中。


iru*_*yak 5

如果没有其他 2 个概念,就很难回答这个问题:iterableiterator protocol.

  1. iterator和 和有什么不一样iterable?从概念上讲,您iterable可以在相应的iterator. 有一些区别,可以帮助区分iteratoriterable实践:
    • 一个区别是iterator__next__方法,iterable没有。
    • 另一个区别 - 它们都包含__iter__方法。在这种情况下,iterable它返回相应的迭代器。在这种情况下,iterator它会自行返回。这可以帮助区分iteratoriterable实践。
>>> x = [1, 2, 3]
>>> dir(x) 
[... __iter__ ...]
>>> x_iter = iter(x)
>>> dir(x_iter)
[... __iter__ ... __next__ ...]
>>> type(x_iter)
list_iterator
Run Code Online (Sandbox Code Playgroud)
  1. 什么是iterablespythonlist, string,range等等是什么iteratorsenumerate, zip,reversed等等。我们可以使用上述方法进行检查。这有点令人困惑。如果我们只有一种类型可能会更容易。有什么区别rangezip?这样做的原因之一 -range有很多附加功能 - 我们可能会对其进行索引或检查它是否包含某些数字等(请参阅此处的详细信息)。

  2. 我们怎样才能创造一个iterator自己?理论上我们可以实现Iterator Protocol(见这里)。我们需要编写__next____iter__方法并引发StopIteration异常等等(有关示例和可能的动机,请参见 Alex Martelli 的回答,另请参见此处)。但实际上我们使用生成器。到目前为止,它似乎是iteratorspython.

我可以给你一些更有趣的例子,展示这些概念在实践中的一些令人困惑的用法:

  • keras我们有tf.keras.preprocessing.image.ImageDataGenerator;这个类没有__next____iter__方法;所以它不是迭代器(或生成器);
  • 如果你调用它的flow_from_dataframe()方法,你会得到DataFrameIterator那些方法;但它没有实现StopIteration(这在 中的内置迭代器中并不常见python);在文档中,我们可能会读到“A DataFrameIteratoryielding tuples of (x, y)”——再次混淆了术语的使用;
  • 我们也有Sequence类 in keras,这是生成器功能的自定义实现(常规生成器不适合多线程),但它没有实现__next__and __iter__,而是围绕生成器的包装器(它使用yield语句);


Man*_*ngo 5

可迭代对象是可以(自然地)迭代的对象。然而,要做到这一点,您将需要类似迭代器对象的东西,并且,是的,术语可能会令人困惑。可迭代对象包含一个__iter__方法,该方法将返回可迭代对象的迭代器对象。

\n

迭代器对象是实现迭代器协议(一组规则)的对象。在这种情况下,它至少必须有这两个方法:__iter____next__。该__next__方法是一个提供新值的函数。该__iter__方法返回迭代器对象。在更复杂的对象中,可能有一个单独的迭代器,但在更简单的情况下,__iter__返回对象本身(通常是return self)。

\n

一个可迭代对象就是一个list对象。它\xe2\x80\x99不是迭代器,但它有一个__iter__返回迭代器的方法。您可以直接调用此方法 as things.__iter__(),或使用iter(things).

\n

如果你想迭代任何集合,你将需要使用它的迭代器:

\n
things_iterator = iter(things)\nfor i in things_iterator:\n    print(i)\n
Run Code Online (Sandbox Code Playgroud)\n

然而,Python 会自动使用迭代器,这就是为什么你永远不会看到上面的例子。相反,你写:

\n
for i in things:\n    print(i)\n
Run Code Online (Sandbox Code Playgroud)\n

自己编写迭代器可能很乏味,因此 Python 有一个更简单的替代方案:生成器函数。生成器函数不是普通函数。代码被推迟,函数立即返回并带有生成器对象,而不是运行代码并返回最终结果。

\n

生成器对象类似于迭代器对象,因为它实现了迭代器协议。对于大多数用途来说,\xe2\x80\x99 已经足够了。其他答案中有很多生成器的例子。

\n

简而言之,迭代器是一个对象,它允许您迭代另一个对象,无论它是集合还是其他值源。生成器是一个简化的迭代器,它执行或多或少相同的工作,但更容易实现。

\n

通常,如果\xe2\x80\x99 满足你的所有需要​​,你会选择一个发电机。但是,如果您\xe2\x80\x99 正在构建一个更复杂的对象,其中包括其他功能之间的迭代,那么您将使用迭代器协议。

\n