迭代器
迭代的含义
- 重复
- 下一次重复是基于上一次的结果
可迭代对象和迭代器
python中,对于有序对象,比如列表、元组、字符串,可以通过索引的方式来遍历元素。像字典和集合这样的无序的对象,没办法索引。
为了提供一种不依赖于索引的迭代方式,python为一些对象内置了obj.__iter__
方法:有这种方法的obj称为可迭代对象。一个可迭代对象执行
obj.__iter__()
得到的结果就是迭代器;iter(obj)等价于obj.__iter__()
迭代器有
__iter__
和__next__
两种方法。对一个迭代器执行Iterator.__iter__()
方法得到这个迭代器本身。对一个迭代器每执行一次Iterator.__next__()
方法,就可以迭代出一个元素。如何判断一个对象是否是可迭代对象或迭代器:
from collections import Iterable, Iterator # 导入模块
isinstance(obj, Iterable) # 判断 obj 是否是 Iterable 的实例,返回True/False
isinstance(obj, Iterator) # 判断 obj 是否是 Iterator 的实例,返回True/False
迭代器的优点
- 提供了一种不依赖于索引的取值方式
- 惰性计算,节省内存
迭代器的缺点
- 取值不如按照索引取值方便
- 一次性的,只能往后走,不能往前退,调用
obj.__next__()
方法执行,最后一个元素迭代完后抛出StopIteration - 因此,也无法获取长度
for i in obj:
for i in obj: # 执行 obj = obj.__iter__()
print(i) # 执行obj.__next__(),把每一次的返回值给i 并打印出来,直到for捕获到StopIteration,结束
知道了这个原理后,我们也可以用while方式来实现迭代。
l = [1,2,3,4]
l = iter(l) # 将列表转化为迭代器
while True:
print(next(l)) # 调用next方法迭代元素,直到抛出StopIteration
为了避免抛出StopIteration,可以使用try:
exception :
捕获它,并设置处理方式:
l = [1,2,3,4]
l = iter(l)
while True:
try: # 在可能出现异常的语句前设置try:
print(next(l))
except StopIteration: # 捕捉StopIteration异常
break # 异常处理
生成器
生成器函数:
函数体包含yield关键字,该函数的执行结果是生成器函数。
def step():
print('first-------->')
yield 1
print('second-------->')
yield 2
print('third-------->')
yield 3
print('fourth-------->') # 函数最后没有return,默认执行return None
# 执行函数step()就是一个生成器
print(type(step())) # 打印结果为:<class 'generator'>
g = step()
from collections import Iterator
print(isinstance(g, Iterator))
# 打印结果是True, 因此生成器就是迭代器,具有迭代器的方法 __next__
print(next(g)) # 执行一次next(g),(等价于g.__next__()),触发一次函数执行,直到遇到yield,打印出yield返回值
print(next(g)) #
print(next(g))
print(next(g))
print(next(g))
yield的功能
- 与return类似,都可以返回值,但通过yield可以返回多次值,而return只能返回一次。
- 为函数封装
__iter__
和__next__
方法,把函数的执行(函数名+())结果做成了迭代器。 - 遵循迭代器的取值方式
obj.__next__()
, 触发函数的执行,函数暂停与再继续的状态由yield保存。
实例:
计数器
def countdown(n):
print('starting counting')
while n > 0:
yield n # 碰到yield暂停,返回当前n的值,下次从当前处往下执行。
# 因为生成器也是迭代器,具有迭代器特性:一次性的,只能往前走
n -= 1
print('stop counting')
g = countdown(5)
print(next(g)) # next迭代g的元素,直到抛出StopIteration,另外,StopIteration的打印位置是随机的。
...
我们也可以用 for i in g
语句来自动捕获 StopIteration
,来结束迭代:
for i in g:
print(i)
# 运行结果如下:
# starting counting
# 5
# 4
# 3
# 2
# 1
# stop counting
注意:通过将生成器函数countdown(5)引用给g, 每次执行g,都是在迭代同一个生成器,返回的值是前后连续的。而每次直接执行countdown(5),都是在产生一个新的生成器。