11.2 迭代访问
11.2.1 迭代(Iteration)
迭代(Iteration)是一种操作过程,指重复获取序列中的元素,直到所有元素被访问完毕的行为。例如前面学过的 for 循环遍历列表就是典型的迭代过程。
11.2.2 可迭代对象(Iterable)
可迭代对象是能够被迭代的对象,即可以通过某种方式返回一个迭代器,供迭代过程使用。
- 本质:实现了 iter() 方法的对象(该方法返回一个迭代器)。
- 常见类型:列表、元组、字符串、字典、集合、文件对象等。
- 特点:可直接用于 for 循环,但自身不存储迭代状态(每次迭代需重新生成迭代器)。
可以通过验证是否是Iterable的实例来判断一个对象是否是可迭代对象:
>>> from collections.abc import Iterable
# 判断一个对象是否是可迭代对象(Iterable)
>>> print(isinstance([1, 2, 3], Iterable))
True
>>> print(isinstance("hello", Iterable))
True
11.2.3 迭代器(Iterator)
迭代器是用于执行迭代的工具,它存储了迭代状态,能通过 next() 方法逐个返回元素。
- 本质:同时实现了 iter() 方法(返回自身)和 next() 方法(返回下一个元素,没有元素时抛出 StopIteration 异常)的对象。
- 特点:
- 惰性计算:不要求事先准备好整个迭代过程中所有的元素。仅仅是在迭代至某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的容器,节省内存。
- 一次性:迭代结束后(抛出 StopIteration),无法重新开始,除非重新创建迭代器。
>>> from collections.abc import Iterator
# 判断一个对象是否是迭代器(Iterator)
>>> print(isinstance([1, 2, 3], Iterator))
False
>>> iter_obj = iter([1, 2, 3])
>>> print(isinstance(iter_obj, Iterator))
True
>>> print(next(iter_obj))
1
>>> print(next(iter_obj))
2
>>> print(next(iter_obj))
3
>>> print(next(iter_obj))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
迭代是遍历元素的过程,由迭代器执行;可迭代对象是能提供迭代器的对象;迭代器是可迭代对象生成的、用于实际迭代的工具。
11.2.4 迭代器类
实现了__iter__()和__next__()方法的类的对象,可以作为迭代器使用,当然也可以用next(对象)的方法来获取一个值。例如:11.1-迭代器类.py
# 迭代器类:实现__iter__()和__next__()
class Fib:
def __init__(self):
self.prev = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
self.prev, self.curr = self.curr, self.prev + self.curr
return self.prev
f = Fib()
for i, v in enumerate(f):
if i < 10:
print(v)
else:
break
结果为:
1
1
2
3
5
8
13
21
34
55
11.2.5 生成器
如果把列表推导式的[]换成(),那么就会成为生成器的表达式。注意生成器也是没有长度的。
>>> g = (x for x in range(10))
>>> next(g)
0
>>> next(g)
1
>>> len(g)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'generator' has no len()
11.2.6 生成器函数
在一个一般函数中使用yield关键字,可以实现一个最简单的生成器,此时这个函数变成一个生成器函数,是一个返回可迭代对象的函数。yield与return都会返回值,区别在于return返回后,函数状态终止;而yield会保存当前函数的执行状态,在下一次调用时,函数又回到之前保存的状态继续执行。
生成器函数与一般函数的不同:
生成器函数包含一个或者多个
yield;当调用生成器函数时,函数将返回一个对象,但是不会立刻向下执行;
像
__iter__()和__next__()方法等是自动实现的,所以我们可以通过next()方法对对象进行迭代;一旦函数被
yield,函数会暂停,控制权返回调用者;局部变量和它们的状态会被保存,直到下一次调用;
函数终止的时候,
StopIteraion会被自动抛出。
例如:11.2-生成器.py
import sys
def fibonacci(n):
""" 使用生成器函数实现斐波那契数列 """
a, b, counter = 0, 1, 0
while True:
counter += 1
if counter > n:
return
yield a
a, b = b, a + b
# f 是一个迭代器,由生成器返回生成
f = fibonacci(10)
# 当函数终止时,处理StopIteration异常
while True:
try:
print(next(f), end=" ")
except StopIteration:
sys.exit()
运行结果为:
0 1 1 2 3 5 8 13 21 34
11.2.7 使用迭代的好处
更容易使用,代码量较小。
内存使用更加高效。因为列表是在建立的时候就分配所有的内存空间,而迭代仅仅是需要的时候才使用,更像一个记录。
代表了一个无限的流。如果我们要读取并使用的内容远远超过内存,但是需要对所有的流中的内容进行处理,那么迭代是一个很好的选择,比如可以让迭代返回当前的处理状态,由于它可以保存状态,所以下一次直接处理即可。