一、请阐述"迭代器对象"与"可迭代对象"的区别与联系
(1)迭代器对象
满足下面三个条件的对象就是迭代器对象。
- 类中同时实现了__iter__ 和 next 方法;
- iter 函数返回self,也就是自身;
- next 函数返回下一个值,如果没有下一个值则抛出StopIteration的异常。
python
class MyRange():
def __init__(self,num):
self.num = num
self.counter = -1 # 注意,counter是从-1开始的,所以第一次调用__next__函数,会返回0
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == self.num: # 如果迭代次数超过num,则触发异常StopIteration,停止迭代,并返回属性counter
raise StopIteration()
return self.counter
obj = MyRange(3) # 设置迭代次数上限是3
print(next(obj)) # 输出0, 等价于 obj.__next__() 都是在调用obj这个实例中的__next__函数,输出下一个数据
print(next(obj)) # 输出1
print(next(obj)) # 输出2
print(next(obj)) # 由于迭代次数上限是3,所以第四次调用__next__函数时会报错
(2)可迭代对象
如果一个类中包含 iter 方法,并且返回一个迭代器,则称这个类创建的对象为可迭代对象。常见的如 list,dict,str 都是可迭代对象,但它们本身不是迭代器。
python
# 可迭代对象
class Foo():
def __iter__(self):
return MyRange(3)
obj1 = Foo() # 则obj1会是一个迭代器对象
# 因为obj1是一个迭代器对象,那么它可以用于for循环
# 在实际操作过程中,首先是通过__iter__获得迭代器对象,然后再通过next函数逐个取值
for item in obj1:
pass
(3)两者的联系
迭代器一定是可迭代对象,但可迭代对象不一定是迭代器。可迭代对象的 iter 方法必须返回一个迭代器。
二、迭代器的优缺点
迭代器的设计哲学是**"用计算换空间"**。理解它的优缺点,是解决在项目中何时使用迭代器等容器类型的关键。
(1)优点
- 惰性求值,节省内存。这是迭代器的核心优点,它不需要一次性将所有数据加载到内存中,而是按需要生成下一个元素。迭代器在内存中相当于只占一个数据的空间,因为每次取值都上一条数据会在内存释放,加载当前的此条数据。
- 支持无限序列。由于不需要预知长度或存储所有元素,迭代器可以表示数学上无限的概念。
(2)缺点
- 单向、一次性消耗。迭代器因为有next函数,所有只能向前推进,不能回退、不能重置、不能重复遍历。一旦耗尽抛出StopIteration,就永久失效。
- 不支持随机访问和长度查询。
三、for循环的本质
for循环的工作原理可以描述为如下动作:
- 使用 iter 获得可迭代对象的迭代器;
- 再反复对迭代器使用 next ;
- 捕获StopIteration异常,退出循环。