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

十年热恋 提交于 2020-02-25 21:03:43

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


#1楼

迭代器:

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

发电机:

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

生成器函数(如以下示例中的ex: foo()函数)返回的生成器对象(如以下示例中的ex: f next()上的每个next()方法调用,都会按顺序生成下一个值。

调用生成器函数时,它甚至不开始执行该函数就返回生成器对象。 首次调用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
>>>

#2楼

iterator是更一般的概念:任何对象的类具有next方法( __next__在Python 3)和__iter__ ,做方法return self

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

当您需要一个具有某些复杂的状态维护行为的类时,或者想要暴露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 3
       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

如果您在应用程序中实际需要这种额外功能。


#3楼

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

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

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

def a_function():
    "when called, returns generator object"
    yield

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

a_generator = (i for i in range(0))

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

生成器迭代器

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

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

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

>>> 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() [Python 3]方法中的__next__()的迭代器对象(从技术上讲,是生成器对象)。 有关生成器的更多信息,可以在yield表达式的文档中找到。

(已添加重点。)

因此,我们从中了解到生成器是(便捷的)迭代器类型。

示例迭代器对象

您可以通过创建或扩展自己的对象来创建实现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迭代器或考虑Generator表达式的函数。

最后,请注意,生成器提供了更多的协同程序功能。 我将在我对“ yield”关键字有什么作用?”的回答中深入解释Generators以及yield语句。


#4楼

添加答案是因为现有答案中没有一个专门解决官方文献中的混乱。

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

函数或对象都可以称为“生成器”,具体取决于您阅读的是哪个Python源文档。 Python词汇表显示生成器函数,而Python Wiki隐含生成器对象。 Python教程显着地设法在两个句子之间暗示了这两种用法:

生成器是用于创建迭代器的简单而强大的工具。 它们的编写方式与常规函数类似,但是只要要返回数据就使用yield语句。 每次在其上调用next()时,生成器都会从上次中断的地方继续(它会记住所有数据值以及最后执行的语句)。

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

尽管存在所有这些困惑,但您仍然可以找到Python语言参考来获得清晰明确的词:

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

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

因此,在正式和精确的用法中, “生成器”不合格表示生成器对象,而不是生成器功能。

上面的参考是针对Python 2的,但是Python 3语言参考却说了同样的话。 但是, Python 3词汇表指出

generator ...通常指生成器函数,但在某些情况下可能指代生成器迭代器。 在预期含义不明确的情况下,使用完整术语可以避免歧义。


#5楼

生成器功能,生成器对象,生成器:

Generator函数就像Python中的常规函数​​一样,但是包含一个或多个yield语句。 生成器函数是一个很好的工具,它可以尽可能轻松地创建Iterator对象。 通过generator函数返回的Iterator对象也称为Generator对象Generator

在此示例中,我创建了一个Generator函数,该函数返回Generator对象<generator object fib at 0x01342480> 。 就像其他迭代器一样,Generator对象可以在for循环中使用,也可以与内置函数next() ,该函数从generator返回下一个值。

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
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!