Python3 迭代器与生成器
目录
迭代
如果给定一个list或tuple,我们可以通过for
循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。
Python的for
循环抽象程度要高于C的for
循环,因为Python的for
循环不仅可以用在list或tuple上,还可以用在其他可迭代对象上。
只要是可迭代对象(Iterable),就可以进行迭代,比如dict就可以迭代:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
d = {"name": "Jane", "age": 18, "sex": "woman"}
print("打印字典中的keys:")
for k in d: # 默认情况下,字典d迭代的是key
print(k)
print("\r")
print("打印字典中的values:")
for v in d.values(): # values()返回一个包含字典中values的迭代器
print(v)
print("\r")
print("打印字典中的(key,value):")
for i in d.items(): # items()返回一个列表,字典中的(键,值)以元组的形式作为该列表中的元素
print(i)
print("\r")
print("打印字典中的key:value:")
for k, v in d.items():
print(k, v, sep=":")
因为dict的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样。
默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values()
,如果要同时迭代key和value,可以用for k, v in d.items()
。
由于字符串也是可迭代对象,因此,也可以作用于for
循环:
>>> for ch in 'ABC':
... print(ch)
...
A
B
C
所以,任何可迭代对象都可以作用于for
循环,包括我们自定义的数据类型,只要符合迭代条件,就可以使用for
循环。
通过collections模块的Iterable类型,可以判断一个对象是否是可迭代对象:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False
迭代器(Iterator)
迭代器是一个对象,简称为迭代器对象(Iterator)。迭代器从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不能后退。
通过collections模块的Iterator类型,可以判断一个对象是否是迭代器:
>>> from collections import Iterator
>>> isinstance([1,2],Iterator)
False # 列表不是一个迭代器对象
迭代器有两个基本的函数:iter() 和 next()
这两个函数都是Python3的内置函数。
iter():用于创建迭代器对象。例如:
tuple、list、dict、str等虽然是Iterable(可迭代对象),但却不是Iterator(迭代器对象)。把list、dict、str等Iterable变成Iterator可以使用iter():
>>> isinstance(iter([1,2]),Iterator)
True
为什么list、dict、str等数据类型不是Iterator?
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
next():可以不断调用迭代器对象并返回下一个值,直到最后抛出StopIteration
错误表示无法继续返回下一个值。例如:
>>>list=[1,2,3,4]
>>> it = iter(list) # 创建迭代器对象
>>> print (next(it)) # 输出迭代器的下一个元素
1
>>> print (next(it))
2
>>>
迭代器对象可以使用for语句进行遍历:
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
list=[1,2,3,4]
it = iter(list) # 创建迭代器对象
for x in it:
print (x, end=" ")
小结
凡是可作用于for循环的对象都是Iterable类型(可迭代对象);
凡是可作用于next()函数的对象都是Iterator类型(迭代器对象),它们表示一个惰性计算的序列;
集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
生成器(generator)
在 Python 中,使用了 yield 关键字的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作。更简单点理解生成器就是一个迭代器,可作用于for循环。
调用生成器函数:在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
调用一个生成器函数,返回的是一个迭代器对象。
举个简单的例子,定义一个generator,依次返回数字1,3,5:
def odd():
print('step 1')
yield 1
print('step 2')
yield(3)
print('step 3')
yield(5)
调用该generator时,首先要生成一个generator对象,然后用next()函数不断获得下一个返回值:
>>> o = odd()
>>> next(o)
step 1
1
>>> next(o)
step 2
3
>>> next(o)
step 3
5
>>> next(o)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
可以看到,odd不是普通函数,而是generator,在执行过程中,遇到yield就中断,下次又继续执行。执行3次yield后,已经没有yield可以执行了,所以,第4次调用next(o)就报错。
我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:
>>>for x in odd():
... print(x)
Python的for
循环本质上就是通过不断调用next()
函数实现的,例如:
for x in [1, 2, 3, 4, 5]:
pass #空语句,即什么都不执行
实际上完全等价于:
# 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break
小结
理解generator的工作原理,它是在for循环的过程中不断计算出下一个元素,并在适当的条件结束for循环。对于函数改成的generator来说,遇到return语句或者执行到函数体最后一行语句,就是结束generator的指令,for循环随之结束。