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

Python生成器和迭代器之间的区别,python,iterator,generator,Python,Iterator,Generator,迭代器和生成器之间的区别是什么?一些关于何时使用每种情况的示例会很有帮助。迭代器是一个更一般的概念:其类在Python 2中有一个uuu next uu方法next和一个确实返回self的uu iter u方法的任何对象 每个生成器都是迭代器,但反之亦然。生成器是通过调用一个函数构建的,该函数在Python 2.5及更早版本中具有一个或多个yield表达式yield语句,并且是一个满足上一段迭代器定义的对象 >>> import collections, types >&

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

迭代器是一个更一般的概念:其类在Python 2中有一个uuu next uu方法next和一个确实返回self的uu iter u方法的任何对象

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

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True
当您需要具有某种复杂的状态维护行为的类时,或者想要公开除uu next uuu和u iter uu和u init uu之外的其他方法时,您可能希望使用自定义迭代器,而不是生成器。大多数情况下,生成器有时候,出于足够简单的需要,生成器表达式就足够了,而且编写代码也更简单,因为在合理的限制范围内的状态维护基本上是通过暂停和恢复帧来完成的

例如,生成器,例如:

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)
但是,当然,有了类正方形,你可以很容易地提供额外的方法,即

    def current(self):
       return self.start
如果您在应用程序中实际需要此类额外功能。

迭代器:

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

发电机:

生成器是一个函数,它使用yield方法生成或产生一系列值

下一次对generator objectfor ex:f的方法调用(如下面示例中的示例所示)由generator functionfor ex:foo函数返回,将按顺序生成下一个值

调用generator函数时,它返回一个generator对象,甚至不开始执行该函数。当第一次调用下一个方法时,函数开始执行,直到到达返回所产生值的yield语句。收益率跟踪,即记住最后一次执行。第二个下一个调用将从上一个值继续

下面的示例演示了生成器对象上的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
>>>
迭代器和生成器之间的区别是什么?一些关于何时使用每个案例的示例会有所帮助。 总之:迭代器是在Python2方法中具有_iter_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。生成器提供了一种简单的内置方法来创建迭代器实例

其中包含yield的函数仍然是一个函数,在调用时返回生成器对象的实例:

def a_function():
    "when called, returns generator object"
    yield
生成器表达式还返回生成器:

a_generator = (i for i in range(0))
>>> 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'>
要获得更深入的阐述和示例,请继续阅读

生成器是迭代器 具体来说,生成器是迭代器的一个子类型

>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True
我们可以用几种方法创建生成器。一种非常常见且简单的方法是使用函数

具体而言,函数中包含yield的函数是一个函数,调用时返回生成器:

a_generator = (i for i in range(0))
>>> 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'>
迭代器是一个Iterable 迭代器是一个Iterable

>>> issubclass(collections.Iterator, collections.Iterable)
True
它需要一个返回迭代器的_iter__)方法:

>>> isinstance(a_generator, collections.Iterator)
True
>>> 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__
迭代器需要一个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
当您尝试将对象与for循环一起使用时,将调用_iter__;方法。然后对迭代器对象调用uuu next uuuu方法,以获取循环的每个项。迭代器在耗尽时引发StopIteration,此时不能重用它

从文件中 从内置类型的迭代器类型部分的生成器类型部分:

Python的生成器提供了一种实现迭代器协议的方便方法。如果容器对象的uuu iter_uuu方法实现为生成器,它将自动返回一个迭代器对象,一个生成器对象提供uuu iter_uuu和next[uu next_u,在Python 3中]方法。有关生成器的更多信息,请参见屈服表达式的文档

重点补充

因此,我们从中了解到生成器是一种方便的迭代器类型

迭代器对象示例 您可以通过创建或扩展自己的对象来创建实现迭代器协议的对象

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
或者更简单,生成器表达式的工作原理类似于列表理解:

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对象扩展为可以迭代的对象时,可以直接使用迭代器协议

但是,在绝大多数情况下,最适合使用FEDGE定义一个函数,该函数返回生成器迭代器或考虑生成器表达式。


最后,请注意,生成器作为协程提供了更多的功能

添加一个答案,因为现有答案中没有一个专门解决官方文字中的混淆问题 是的

生成函数是使用收益率而不是收益率定义的普通函数。调用生成器函数时,它返回一个生成器对象,这是一种迭代器——它有一个下一个方法。调用next时,将返回生成器函数生成的下一个值

函数或对象可能被称为生成器,具体取决于您读取的Python源文档。表示生成器功能,而表示生成器对象。在三个句子的空间中,作者成功地暗示了这两种用法:

生成器是创建迭代器的简单而强大的工具。它们的编写方式与常规函数类似,但在需要返回数据时使用yield语句。每次对其调用next时,生成器都会在其停止的位置恢复,并记住所有数据值以及上次执行的语句

前两句用生成器函数标识生成器,第三句用生成器对象标识生成器

尽管存在所有这些困惑,但人们可以找到明确和最终的答案:

屈服表达式仅在定义生成器函数时使用,并且只能在函数定义的主体中使用。在函数定义中使用屈服表达式足以使该定义创建生成器函数而不是普通函数

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

因此,在正式和精确的用法中,generator unqualified是指generator对象,而不是generator函数

上面的参考是针对Python2的,但说明了相同的事情。然而,缔约国表示

发电机。。。通常指生成器函数,但在某些上下文中可能指生成器迭代器。在意图不明确的情况下,使用完整的术语可以避免歧义

生成器函数、生成器对象、生成器:

生成器函数与Python中的常规函数类似,但它包含一个或多个yield语句。生成器函数是一个很好的工具,可以尽可能轻松地创建迭代器对象。生成器函数返回的迭代器对象也称为生成器对象或生成器

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
在这个例子中,我创建了一个生成器函数,它返回一个生成器对象。与其他迭代器一样,生成器对象可以在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

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

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)))

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

每个人都有一个非常好的详细答案,并附有示例,我非常感谢。我只想给那些在概念上还不太清楚的人一个简短的回答:

如果您创建自己的迭代器,它会有点复杂-您有 创建一个类并至少实现iter和next方法。但是,如果您不想经历这些麻烦并希望快速创建迭代器,该怎么办呢。幸运的是,Python提供了一种定义迭代器的捷径。您所需要做的就是定义一个函数,其中至少有一个调用要产生,现在当您调用该函数时,它将返回类似于迭代器的内容,您可以调用下一个方法并在for循环中使用它。这个东西在Python中有一个名为Generator


希望这能澄清一点。

之前的答案忽略了这一点:生成器有一个封闭的方法,而典型的迭代器没有。close方法在生成器中触发StopIteration异常,该异常可能会在该迭代器的finally子句中捕获,以获得运行某些清理的机会。这种抽象使得它在大型迭代器中比在简单迭代器中更有用。可以像关闭文件一样关闭生成器,而不必担心下面的内容

也就是说,我个人对第一个问题的回答是:iteratable只有一个iter方法,典型的迭代器只有一个next方法,生成器既有iter方法,又有next方法,还有一个close方法

对于第二个问题,我个人的答案是:在公共界面中,我倾向于更倾向于生成器,因为它更具弹性:封闭方法具有更大的可组合性,并且从中获得收益。在局部情况下,我可以使用迭代器,但前提是它是一个平面和简单的结构,迭代器不容易组合,并且如果有理由相信序列很短,特别是如果它可能在结束之前停止。我倾向于将迭代器视为低级原语,但视为文本除外

对于控制流问题,生成器与承诺一样重要:两者都是抽象的和可组合的。

强烈建议迭代器和生成器使用的示例 一种没有对偶数执行某些操作的生成器的方法

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)
发电机也用于断开双回路 迭代器

一本满是书页的书是一本易读的书,书签是一本易读的书 迭代器

而这个书签除了下一步移动之外,什么都不用做

要使用生成器。。。我们需要一个函数

要使用迭代器。。。我们需要下一个和iter

如前所述:

生成器函数返回迭代器对象

迭代器的全部好处是:

>>> isinstance(a_generator, collections.Iterator)
True
>>> 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__
每次在内存中存储一个元素


我以一种非常简单的方式专门为Python新手编写,尽管Python在本质上做了很多事情

让我们从最基本的开始:

考虑一份清单

l = [1,2,3]
让我们编写一个等价函数:

>>> all(isinstance(iter(element), collections.Iterator) for element in (
        (), [], {}, set(), frozenset(), '', b'', bytearray(), range(0), memoryview(b'')))
True
def f():
    return [1,2,3]
打印件的o/p:[1,2,3]& 打印件的o/p:[1,2,3]

让我们将列表l设置为iterable:在python中,列表始终是iterable,这意味着您可以随时应用迭代器

让我们在列表上应用迭代器:

iter_l = iter(l) # iterator applied explicitly
让我们把一个函数设为iterable,即写一个等价的生成函数。 在python中,只要引入关键字yield;它将成为一个生成器函数,迭代器将被隐式应用

注意:在应用隐式迭代器的情况下,每个生成器都是可迭代的,这里隐式迭代器是关键 因此,发电机功能将为:

def f():
  yield 1 
  yield 2
  yield 3

iter_f = f() # which is iter(f) as iterator is already applied implicitly
所以,如果你已经观察到,一旦你做了一个发电机的函数,它已经是iterf了

现在,

l是列表,在应用迭代器方法iter后, 伊特尔

在应用迭代器方法iter之后,f已经是iterf了 变成iterf,这又是iterf

这有点像是你将int转换为int,而int已经是int,它将保持为int

例如,o/p:

print(type(iter(iter(l))))

永远不要忘记这是Python,而不是C或C++

因此,上述解释得出的结论是:

列表l~=iterl

发电机函数f==iterf


如果没有其他两个概念,很难回答这个问题:iterable和iterator协议

迭代器和iterable之间的区别是什么? 从概念上讲,您可以借助相应的迭代器对iterable进行迭代。在实践中,有一些差异有助于区分迭代器和iterable: 一个区别是迭代器有_next__方法,而iterable没有。 另一个区别——它们都包含_; iter _;方法。对于iterable,它返回相应的迭代器。对于迭代器,它返回自身。 这有助于在实践中区分迭代器和iterable。 >>>x=[1,2,3] >>>迪克斯 […\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu >>>x_iter=iterx >>>dirx_iter […\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu >>>typex_iter 列表迭代器 python中的可重用性是什么?列表、字符串、范围等。什么是迭代器?枚举、压缩、反转等。我们可以使用上述方法检查这一点。这有点让人困惑。如果我们只有一种类型,可能会更容易。range和zip之间有什么区别吗?这样做的原因之一-范围有很多额外的功能-我们可以索引它或检查它是否包含一些数字等。请参阅详细信息

我们如何自己创建迭代器?理论上我们可以实现迭代器协议。我们需要编写uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。但实际上我们使用发电机。到目前为止,它似乎是python中创建迭代器的主要方法

我可以给大家举几个更有趣的例子,说明这些概念在实践中的用法有些混乱:

在keras中,我们有tf.keras.preprocessing.image.ImageDataGenerator;这个类没有uuu next和uuu iter方法;所以它不是迭代器或生成器; 如果您从数据帧方法调用其流,您将得到具有这些方法的数据帧迭代器;但是它没有实现StopIteration,这在python的内置迭代器中并不常见;在文档中,我们可能会看到一个DataFrameIterator产生x,y元组,这再次混淆了术语的用法; 我们在keras中也有Sequence类,这是生成器功能的自定义实现常规生成器不适合多线程,但它不实现uuu next_uuuuuuuuuuu和uuuuu iter_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;
无代码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.

我不认为生成器函数和生成器对象之间有太多的混淆,因为同样的原因,类和它们的实例之间通常没有混淆。在这两种情况下,您都可以通过调用一个来获取另一个,在随意的对话或快速编写的文档中,您可以使用类名或单词生成器来获取其中一个。您只需要明确说明gener
ator函数和generator对象在极少数情况下,哪一个是重要的。不管为什么不应该出现混淆的理论原因是什么,对这个问题的其他答案的评论都没有解决就互相否定和矛盾,这表明实际的混淆是存在的。2.随意的不精确是可以的,但一个精确的、权威的来源至少应该是这样的选择之一。在我当前的项目中,我广泛使用生成器函数和对象,在设计和编码时,它们之间的区别非常重要。现在知道使用什么术语很好,因此我以后不必更改几十个变量名和注释。想象一下,在数学文献中,函数和返回值之间没有区别。偶尔非正式地将它们合并在一起是很方便的,但这会增加各种错误的风险。如果这种区别没有用惯例、语言和符号形式化,那么高等现代数学将受到明显的、不必要的阻碍。高阶函数在生成器或生成器函数中的传递可能听起来很奇怪,但对我来说,它们已经出现了。我在ApacheSpark工作,它实施了一种非常实用的编程风格。函数必须创建、传入和传出各种对象才能完成任务。我曾经遇到过很多情况,我不知道我使用的是哪种发电机。变量名和注释中的提示,使用一致且正确的术语,有助于消除混淆。一个Pythonist的默默无闻可以成为另一个Pythonist项目设计的中心@保罗,谢谢你写下这个答案。这种混淆很重要,因为生成器对象和生成器函数之间的区别在于获得所需的行为和必须查找生成器之间的区别。仅供参考,收益率不是方法,而是关键字。在谈论合成时,你能举个例子来说明你的意思吗?另外,当谈到典型的迭代器时,你能解释一下你的想法吗?另一个答案是迭代器是一个iterable。既然一个iterable有一个_uiter _uuu方法,为什么迭代器只能有_unext uu?如果它们应该是iterables,我希望它们也一定有iter。@bli:AFAIC这个答案在这里,所以它是正确的,而另一个答案指的是一些实现,所以它是可疑的。该标准只需要iterables上的uuu iter_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。请不要将duck类型的标准与其实现混淆,因为特定的Python解释器是如何实现的。这有点像生成器函数定义和生成器对象实现之间的混淆;关于您的第一个代码段,我想知道除了列表[]之外,arg'stream'还可以是什么?