Last Updated on

Python中,迭代器和生产器的概念和使用很多,但是也很容易混淆,搞不清楚,下面我们就来详细理解一下迭代器和生成器。

Iterables

要了解迭代器,需要先了解一个概念,Iterables:可迭代的!

当我们创建列表时,可以一一获取它的内容项。逐一读取其项,例如:

mylist = [1, 2, 3]
for i in mylist:
    print(i)

# 输出结果:
1
2
3

那么mylist就是一个可迭代的对象。在Python中有很多都是可迭代对象,像liststringtupledict…等等

那么可迭代对象就是迭代器吗? 并不是!

可以说能使用“ for... in...”的所有对象都是可迭代的对象,为什么呢,因为可迭代对象是定义了可返回迭代器的__iter__方法的对象。而”for...in...“就是将可迭代对象通过__iter__方法生成迭代器然后对迭代器不断的间隙next()操作,再处理掉最后一次对迭代器next()时抛出的异常。

看下面示例:

from collections import Iterable
from collections import Iterator

a = [1,2,3]

print(isinstance(a, Iterable))
print(isinstance(a, Iterator))
print(next(a))

b = iter(a)
print(isinstance(b, Iterable))
print(isinstance(b, Iterator))
print(next(b))
print(next(b))
print(next(b))
print(next(b))


# 输出结果:
True
False
抛出 TypeError: 'list' object is not an iterator 错误

True
True
1
2
3
抛出 StopIteration 错误

如上所示,可以看出,列表a是一个可迭代对象,但不是一个迭代器,可以用内置方法iter()来生成一个迭代器。你会发现迭代器也是一个可迭代的对象。迭代器可以使用next()方法来输出下一个元素。而非迭代器,不能使用next()方法。

Iterator

Iterator:迭代器。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退

迭代器有两个基本的方法:__iter__() 和 __next__()

迭代器不会一次性把所有元素加载到内存,而是需要的时候才生成返回结果

因为实现了__iter__方法,所以迭代器也是一个可迭代对象。

看下面示例:

from collections import Iterable
from collections import Iterator

a = [1,2,3]
b = iter(a)
print(isinstance(b, Iterable))
print(isinstance(b, Iterator))
print(next(b))
print(next(b))
print(next(b))
print(next(b))

# 输出结果:
True
True
1
2
3
抛出 StopIteration 错误

迭代器每次调用next()方法的时候做两件事:

  1. 为下一次调用next()方法修改状态
  2. 生成当前调用的返回结果

这些可迭代的方法很方便,因为您可以随意读取它们,但是您在迭代这些可迭代对象时,虽然迭代器不会一次性把所有元素加载到内存,但是可迭代的对象,如常见的list,dict等,当可迭代对象拥有很多值时,先定义可迭代对象,再进行迭代这就种处理方式就不是我们想要的,会占用大量内存空间。

为了解决这种烦劳,于是有了生成器。

Generators

生成器是迭代器的一种迭代,是一种特殊的迭代器,它具有以下几个特点:

  • 只能迭代一次
  • 生成器不会将所有值存储在内存中,它们会即时生成值
  • 生成器可以传入数据。

看下面示例:

from collections import Iterator, Iterable

# 生成器可以使用如下的生成器表达式创建
mygenerator = (x*x for x in range(3))

print(next(mygenerator))

for i in mygenerator:
    print(i)

print(isinstance(mygenerator, Iterable))
print(isinstance(mygenerator, Iterator))

# 输出结果:
0
1
4
True
True

生成器是特殊的迭代器,所以也算是迭代器的一种,生成器也是可迭代的。生成器也可以使用next()方法来获取下一个值,但是生成器只能迭代一次。无法重复迭代这是需要注意的。


生成器函数

yield是与关键字return一样使用的,不同之处在于该函数将返回生成器。那么这个函数就是生成器函数。也就是说有关键字yield的函数就是生成器函数。

def createGenerator():
    mylist = range(3)
    for i in mylist:
        yield i*i

mygenerator = createGenerator()

print(mygenerator)
for i in mygenerator:
    print(i)


# 输出结果:
<generator object createGenerator at 0x1061f4f10>
0
1
4

要掌握yield,您必须了解在调用函数时,在函数主体中编写的代码不会运行。该函数仅返回生成器对象!

仔细看下面这段说明:

当你第一次使用for调用你的生成器时,它将从头开始运行函数中的代码,直到命中yield,然后返回第一个yield的值。然后当你再此迭代时将从上一个yield中断处继续运行,并直到遇到下一个yield,返回yield的值,而不是从头运行! 一直这样,知道没有yield值可返回为止。 且由于生成器只能迭代一次,所以如果生成器是空的,则可能是由于生成器函数存在问题,导致程序遇不到yield的值,或者是因为迭代已经结束。

看示例:

def createGenerator(name):
    yield 1
    yield 2
    yield 3
    if name == 'amos':
        yield 666

mygenerator = createGenerator('amos')

print(mygenerator)
for i in mygenerator:
    print(i)

# 输出结果:
<generator object createGenerator at 0x10732df10>
1
2
3
666

这样也可以是一个生成器。理解了yield在生成器函数中的作用就可以更好的编写自己的生成器函数。

有任何问题,欢迎留言讨论