一、迭代器
迭代器的定义
迭代器是一个实现了迭代器协议的对象。迭代器协议规定了一个对象必须实现两个方法:iter() 和 next()。然而,需要注意的是,从严格意义上讲,一个迭代器对象只需要实现 next() 方法,因为 iter() 函数(用于获取迭代器)会自动调用对象的 iter() 方法(如果该方法存在),而该方法通常返回对象自身。因此,在实际应用中,我们通常将只实现了 next() 方法的对象称为迭代器。
迭代器协议
迭代器协议是指迭代器对象必须遵循的一组规则
iter() 方法:
- 返回迭代器对象本身。
- 在Python中,如果一个对象实现了 iter() 方法,那么它就被认为是可迭代的(iterable)。
- 但对于迭代器对象而言,iter() 方法通常只是简单地返回 self。
next() 方法:
- 返回序列中的下一个元素。
- 如果序列中没有更多元素可供返回,则该方法应该抛出 StopIteration 异常,以通知迭代器的使用者迭代已经完成。
迭代器的特点
- 惰性求值:迭代器采用惰性求值的方式,即只有在需要时才生成值。这有助于节省内存,特别是在处理大型数据集或无限序列时。
- 状态跟踪:迭代器内部维护了一个状态,用于跟踪当前迭代到的位置。这确保了每次调用 next() 方法时,都会返回序列中的下一个元素。
- 单次遍历:迭代器只能向前移动,不能回退。一旦迭代器耗尽(即抛出 StopIteration 异常),它将无法再次使用来遍历序列。
迭代器的使用
在Python中,你可以通过调用对象的 iter() 方法(或更常见的是使用内置的 iter() 函数)来获取一个迭代器。然后,你可以使用 next() 函数(或在循环中直接使用迭代器)来逐个访问元素,直到捕获到 StopIteration 异常为止。
迭代器的优势
- 节省内存:由于迭代器采用惰性求值的方式,它们可以在需要时才生成值,从而节省内存。
- 支持无限序列:迭代器可以处理无限序列,因为它们是按需生成值的。
- 解耦迭代逻辑:迭代器将迭代逻辑与容器类型分离,使得不同的容器类型可以共享相同的迭代逻辑。
iter()
在Python中,iter() 函数是一个内置函数,用于获取一个迭代器。迭代器是一个实现了迭代协议的对象,它允许你逐个访问容器(如列表、元组、字符串等)中的元素,而无需一次性在内存中加载所有元素。这使得迭代器在处理大型数据集时非常高效。
基本用法
iterator = iter(iterable)
iterator = iter(callable, sentinel)
参数:
- iterable:一个可以迭代的对象,如列表、元组、字符串、字典、集合等。
- callable:一个无参数的可调用对象(如函数),每次调用时返回迭代器的下一个值,直到它返回 sentinel。
- sentinel:一个标记值,用于指示迭代结束。当 callable 返回这个值时,迭代停止。
- 返回值:返回一个迭代器对象。
案例
使用 iter() 获取迭代器
my_list = [1, 2, 3, 4, 5]
iterator = iter(my_list)
# 使用 next() 函数遍历迭代器
while True:
try:
value = next(iterator)
print(value)
except StopIteration:
break
输出
1
2
3
4
5
使用 iter() 和 callable、sentinel
def my_generator():
count = 0
while True:
yield count
count += 1
# 创建一个迭代器,当生成器返回特定值时停止迭代
iterator = iter(my_generator(), 5)
# 使用 next() 函数遍历迭代器
while True:
try:
value = next(iterator)
print(value)
except StopIteration:
break
输出
0
1
2
3
4
在这个例子中,my_generator() 是一个生成器函数,它每次调用 next() 时返回一个值。我们使用 iter() 函数和 sentinel 参数来创建一个迭代器,当生成器返回 5 时停止迭代。
超出数据条数会报错
my_list = [1, 2, 3, 4, 5]
iterator = iter(my_list)
print(next(iterator)) # 输出:1
print(next(iterator)) # 输出:2
print(next(iterator)) # 输出:3
print(next(iterator)) # 输出:4
print(next(iterator)) # 输出:5
#再输出就没有值了,就会出现报错
print(next(iterator))
#报错信息如下:
# print(next(iterator))
# StopIteration
注意事项
- 迭代器只能向前移动,不能回退。
- 一旦迭代器被耗尽(即所有元素都被访问过),它就不能再产生新的值。如果你尝试再次使用耗尽的迭代器,它将立即引发 StopIteration 异常。
- 使用 iter() 和 next() 手动迭代容器是一种低级的迭代方式。在大多数情况下,你更可能使用循环(如 for 循环)来自动处理迭代。
自定义迭代器类
class MyRange:
"""
一个自定义的迭代器类,用于生成从start到end(不包括end)的数字。
"""
def __init__(self, start, end):
"""
初始化方法。
:param start: 起始数字
:param end: 结束数字(不包括)
"""
self.current = start # 当前数字,从start开始
self.end = end # 结束数字,迭代到这个数字之前停止
def __iter__(self):
"""
使对象成为迭代器的方法(实现迭代协议的一部分)。
:return: 返回迭代器对象自身
"""
return self
def __next__(self):
"""
返回迭代器的下一个值。
:return: 下一个数字,如果到达end则引发StopIteration异常
"""
if self.current < self.end:
num = self.current # 存储当前数字
self.current += 1 # 移动到下一个数字
return num # 返回存储的数字
else:
raise StopIteration # 如果到达或超过end,则停止迭代
# 示例运行代码
if __name__ == "__main__":
# 创建一个MyRange对象,表示从0到5的数字
my_range = MyRange(0, 5)
# 使用for循环遍历迭代器
print("Using for loop:")
for number in my_range:
print(number)
# 手动使用next()函数遍历迭代器
print("\nUsing next() function:")
iterator = iter(my_range) # 注意:这里会重新初始化迭代器,但my_range对象已经耗尽,所以下面会立即引发StopIteration
# 由于上面的for循环已经耗尽了迭代器,我们需要重新创建一个MyRange对象来演示next()的使用
iterator = iter(MyRange(0, 5))
while True:
try:
number = next(iterator)
print(number)
except StopIteration:
break
运行结果:
Using for loop:
0
1
2
3
4
Using next() function:
0
1
2
3
4
- 在上面的代码中,MyRange 类实现了两个特殊方法:iter() 和 next(),这使得它的实例可以作为迭代器使用。
- iter() 方法返回迭代器对象自身,这是实现迭代协议的要求之一。
- next() 方法返回迭代器的下一个值,并在没有更多值可供返回时引发 StopIteration 异常。
- 在示例运行代码中,首先使用 for 循环遍历了 MyRange 对象。由于迭代器是惰性求值的,并且只能遍历一次,因此第二次尝试使用 next() 函数遍历时,我们重新创建了一个 MyRange 对象。
- 在实际应用中,你可能希望设计一个更加健壮的迭代器类,比如能够处理反向迭代、步长等。这个示例仅展示了最基本的功能。