一、迭代器基础入门
1.1 核心概念:迭代、可迭代对象、迭代器
1.1.1 什么是迭代?
迭代就是重复执行某个操作,依次获取序列/集合中的每一个元素 的过程。我们最常用的for...in...循环遍历数据的过程,本质就是迭代。
# 最常见的迭代:for循环遍历列表
nums = [1, 2, 3, 4, 5]
# 迭代过程:依次取出nums中的每个元素,赋值给num,执行循环体
for num in nums:
print(num)
# 输出:
# 1
# 2
# 3
# 4
# 5
注意:不是所有对象都能被迭代,比如数字int、浮点数float就无法被for循环遍历,会直接报错。
# 错误示例:非可迭代对象无法被迭代
weight = 160
for item in weight:
print(item)
# 报错:TypeError: 'int' object is not iterable
1.1.2 可迭代对象(Iterable)
通俗定义 :凡是能被for...in...循环遍历的对象,都是可迭代对象。
官方定义 :实现了__iter__()魔法方法的对象,就是可迭代对象。
Python中常见的可迭代对象:
- 序列类型:列表
list、元组tuple、字符串str - 集合类型:集合
set、字典dict - 文件对象:
open()打开的文件句柄 - 特殊对象:
range()对象、map/filter/zip等函数返回的对象
1.1.3 迭代器(Iterator)
通俗定义 :一个能记住遍历位置 的智能书签,它可以逐个返回序列中的元素,直到所有元素遍历完毕。
官方定义 :同时实现了__iter__()和__next__()两个魔法方法的对象,就是迭代器,这两个方法合称为迭代器协议。
1.1.4 三者核心区别对比表
|--------------------|---------------------|---------------------------|
| 特性 | 可迭代对象 | 迭代器 |
| 必须实现__iter__()方法 | ✅ 是 | ✅ 是 |
| 必须实现__next__()方法 | ❌ 否 | ✅ 是 |
| 能被for循环遍历 | ✅ 是 | ✅ 是 |
| 能用next()函数获取元素 | ❌ 否(需先转迭代器) | ✅ 是 |
| 可多次遍历 | ✅ 是(每次遍历生成新迭代器) | ❌ 否(一次性,遍历完即耗尽) |
| 内存占用 | 一次性加载所有元素 | 惰性加载,仅生成当前元素 |
| 常见示例 | list、str、dict、tuple | iter(list)、map、filter、zip |
1.1.5 核心关系
可迭代对象 → 调用iter()函数 → 生成迭代器 → 调用next()函数 → 逐个获取元素
1.2 for循环的底层原理(迭代的本质)
我们天天用的for循环,底层完全是基于迭代器实现的,它的执行流程可以拆解为以下4步:
- 调用
iter(可迭代对象),获取该对象的迭代器; - 循环调用
next(迭代器),依次获取下一个元素; - 成功获取元素后,执行循环体;
- 当没有更多元素时,捕获
StopIteration异常,结束循环。
我们用代码完全复刻for循环的底层执行逻辑:
# 原始for循环
nums = [10, 20, 30]
print("原始for循环执行结果:")
for num in nums:
print(num)
# 复刻for循环的底层实现
print("\n复刻for循环底层逻辑执行结果:")
# 1. 调用iter()获取列表的迭代器
nums_iterator = iter(nums)
while True:
try:
# 2. 循环调用next()获取下一个元素
num = next(nums_iterator)
# 3. 执行循环体
print(num)
except StopIteration:
# 4. 捕获异常,结束循环
break
# 两段代码输出完全一致
1.3 核心内置函数:iter() 与 next()
这两个函数是操作迭代器的核心,所有迭代操作都基于这两个函数实现。
1.3.1 iter() 函数:将可迭代对象转为迭代器
语法 :iter(iterable)
核心作用 :接收一个可迭代对象,返回该对象对应的迭代器;如果传入的不是可迭代对象,直接抛出TypeError。
完整代码示例:
import re
def test_iter_function():
"""测试iter()函数的完整用法"""
print("========== iter()函数测试 ==========")
# 1. 列表(可迭代对象)转迭代器
list1 = [1, 2, 3]
list_iter = iter(list1)
print(f"1. 列表转迭代器:{list_iter}")
print(f" 迭代器类型:{type(list_iter)}")
# 2. 字符串转迭代器
str1 = "python"
str_iter = iter(str1)
print(f"\n2. 字符串转迭代器:{str_iter}")
# 3. 字典转迭代器(默认迭代key)
dict1 = {"name": "张三", "age": 20}
dict_iter = iter(dict1)
print(f"\n3. 字典转迭代器,第一个元素:{next(dict_iter)}")
# 4. 非可迭代对象调用iter()会报错
try:
int1 = 123
int_iter = iter(int1)
except TypeError as e:
print(f"\n4. 非可迭代对象报错:{e}")
# 运行测试
test_iter_function()
1.3.2 next() 函数:获取迭代器的下一个元素
语法 :next(iterator, default=None)
核心作用 :接收一个迭代器,返回迭代器的下一个元素;如果迭代器没有更多元素,有默认值则返回默认值,无默认值则抛出StopIteration异常。
完整代码示例:
def test_next_function():
"""测试next()函数的完整用法"""
print("\n========== next()函数测试 ==========")
# 1. 基础用法:逐个获取迭代器元素
nums = [10, 20, 30]
nums_iter = iter(nums)
print("1. 基础用法:逐个获取元素")
print(f" 第一次next:{next(nums_iter)}")
print(f" 第二次next:{next(nums_iter)}")
print(f" 第三次next:{next(nums_iter)}")
# 2. 无元素时,无默认值会抛出StopIteration
print("\n2. 无元素无默认值的情况")
try:
next(nums_iter) # 已经没有元素了
except StopIteration as e:
print(f" 抛出异常:{e}")
# 3. 无元素时,设置默认值不会报错,返回默认值
print("\n3. 无元素有默认值的情况")
nums_iter2 = iter([1, 2])
next(nums_iter2)
next(nums_iter2)
# 已经没有元素,设置默认值"无更多元素"
print(f" 带默认值的next:{next(nums_iter2, '无更多元素')}")
# 运行测试
test_next_function()
二、迭代器核心协议(魔法方法详解)
迭代器的核心是迭代器协议 ,它规定了一个对象要成为迭代器,必须实现两个魔法方法:__iter__() 和 __next__(),缺一不可。
2.1 iter() 方法
语法 :def __iter__(self):
核心要求 :必须返回一个迭代器对象(通常返回self,也就是对象本身)
核心作用:
- 告诉Python解释器,这个对象是可迭代的;
- for循环、iter()函数会自动调用这个方法,获取迭代器。
2.2 next() 方法
语法 :def __next__(self):
核心要求:
- 必须返回迭代器的下一个元素;
- 当没有更多元素时,必须主动抛出
StopIteration异常,终止迭代。
核心作用:控制迭代的逻辑,记录遍历位置,生成/返回下一个元素,处理迭代终止条件。
2.3 StopIteration 异常
这是迭代终止的唯一标准信号,Python的for循环、迭代相关函数都会自动捕获这个异常来结束迭代,无需我们手动处理。
注意:自定义迭代器时,必须在没有元素时主动抛出这个异常,否则会陷入无限循环!
2.4 核心协议完整代码示例
class SimpleIterator:
"""一个最简单的、符合迭代器协议的自定义迭代器"""
def __init__(self, end_num):
# 初始化:设置迭代的终止数字
self.end_num = end_num
# 记录当前遍历的位置,初始值为0
self.current = 0
def __iter__(self):
"""必须实现的__iter__方法,返回迭代器本身"""
print("调用了__iter__方法")
return self
def __next__(self):
"""必须实现的__next__方法,返回下一个元素,无元素时抛异常"""
print("调用了__next__方法")
# 终止条件:当前数字 >= 终止数字时,抛异常结束迭代
if self.current >= self.end_num:
raise StopIteration
# 记录当前要返回的值
return_value = self.current
# 位置+1,为下一次迭代做准备
self.current += 1
# 返回当前值
return return_value
# 测试自定义迭代器
if __name__ == "__main__":
print("========== 迭代器协议测试 ==========")
# 创建迭代器实例,终止数字为3
my_iter = SimpleIterator(3)
# 1. 用for循环遍历(自动调用__iter__和__next__)
print("1. for循环遍历迭代器:")
for num in my_iter:
print(f" 迭代到:{num}")
# 2. 手动用iter()和next()遍历
print("\n2. 手动调用iter()和next():")
my_iter2 = SimpleIterator(2)
# iter()会自动调用__iter__方法
iter_obj = iter(my_iter2)
# next()会自动调用__next__方法
print(f" 第一次next:{next(iter_obj)}")
print(f" 第二次next:{next(iter_obj)}")
# 第三次next会抛异常
try:
next(iter_obj)
except StopIteration:
print(f" 第三次next:抛出StopIteration异常,迭代结束")
三、自定义迭代器(从入门到实战)
通过实现迭代器协议,我们可以自定义任意逻辑的迭代器,下面从简单到复杂,提供3个完整的自定义迭代器案例,覆盖绝大多数实战场景。
3.1 基础案例:自定义range迭代器
模拟Python内置的range()函数,实现一个自定义的数字序列迭代器,支持起始值、终止值、步长。
class MyRange:
"""自定义range迭代器,支持start、end、step"""
def __init__(self, start, end=None, step=1):
"""
初始化方法
:param start: 起始值
:param end: 终止值(可选,不填则start为0,end为传入的start)
:param step: 步长,默认1,不能为0
"""
# 处理只传一个参数的情况,比如MyRange(5) 等价于 MyRange(0,5)
if end is None:
self.start = 0
self.end = start
else:
self.start = start
self.end = end
# 步长校验
if step == 0:
raise ValueError("步长不能为0")
self.step = step
# 记录当前位置,初始值为start
self.current = self.start
def __iter__(self):
"""返回迭代器本身,符合迭代器协议"""
return self
def __next__(self):
"""核心迭代逻辑,控制步长和终止条件"""
# 终止条件:正步长时,current >= end 终止;负步长时,current <= end 终止
if (self.step > 0 and self.current >= self.end) or (self.step < 0 and self.current <= self.end):
raise StopIteration
# 记录要返回的值
return_value = self.current
# 按步长更新当前位置
self.current += self.step
# 返回值
return return_value
# 测试自定义MyRange迭代器
if __name__ == "__main__":
print("========== 自定义MyRange迭代器测试 ==========")
# 1. 只传终止值
print("1. MyRange(5):")
for num in MyRange(5):
print(f" {num}", end=" ")
print()
# 2. 传起始值和终止值
print("\n2. MyRange(2, 8):")
for num in MyRange(2, 8):
print(f" {num}", end=" ")
print()
# 3. 传起始值、终止值、步长
print("\n3. MyRange(1, 10, 2):")
for num in MyRange(1, 10, 2):
print(f" {num}", end=" ")
print()
# 4. 负步长(倒序)
print("\n4. MyRange(10, 0, -2):")
for num in MyRange(10, 0, -2):
print(f" {num}", end=" ")
print()
3.2 进阶案例:斐波那契数列迭代器
实现一个斐波那契数列迭代器,支持指定最大生成个数,完美体现迭代器惰性计算的核心优势(无需一次性生成所有数列,用的时候才生成)。
class FibonacciIterator:
"""斐波那契数列迭代器"""
def __init__(self, max_count):
"""
初始化方法
:param max_count: 要生成的斐波那契数列的最大个数
"""
self.max_count = max_count # 最大生成个数
self.count = 0 # 记录已经生成的个数
# 斐波那契初始值:a=第0个数,b=第1个数
self.a, self.b = 0, 1
def __iter__(self):
"""返回迭代器本身"""
return self
def __next__(self):
"""生成下一个斐波那契数"""
# 终止条件:生成个数达到最大值
if self.count >= self.max_count:
raise StopIteration
# 记录当前要返回的数
current_num = self.a
# 更新斐波那契数列:a变成b,b变成a+b
self.a, self.b = self.b, self.a + self.b
# 生成个数+1
self.count += 1
# 返回当前数
return current_num
# 测试斐波那契迭代器
if __name__ == "__main__":
print("========== 斐波那契数列迭代器测试 ==========")
# 生成前10个斐波那契数
fib_iter = FibonacciIterator(10)
print("前10个斐波那契数:")
for num in fib_iter:
print(f" {num}", end=" ")
print()
# 手动逐个获取,体现惰性计算
print("\n手动逐个获取前3个斐波那契数:")
fib_iter2 = FibonacciIterator(10)
print(f" 第1个:{next(fib_iter2)}")
print(f" 第2个:{next(fib_iter2)}")
print(f" 第3个:{next(fib_iter2)}")
3.3 高级案例:可迭代对象与迭代器分离
前面的案例中,一个类同时是可迭代对象和迭代器,会导致无法多次遍历 的问题。实战中更规范的写法是:将可迭代对象和迭代器拆分为两个类 ,每次调用__iter__()都返回一个新的迭代器实例,实现多次遍历。
# 1. 迭代器类:只负责迭代逻辑,实现__iter__和__next__
class StudentIterator:
"""学生列表迭代器,负责具体的迭代逻辑"""
def __init__(self, student_list):
self.student_list = student_list
self.index = 0 # 记录遍历的索引位置
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.student_list):
raise StopIteration
# 获取当前索引的学生
current_student = self.student_list[self.index]
# 索引+1
self.index += 1
# 返回学生信息
return current_student
# 2. 可迭代对象类:只负责生成迭代器,实现__iter__
class StudentList:
"""学生列表可迭代对象,对外暴露,支持多次遍历"""
def __init__(self):
self.students = ["张三", "李四", "王五", "赵六"]
def __iter__(self):
"""每次调用都返回一个全新的迭代器实例,实现多次遍历"""
return StudentIterator(self.students)
# 测试可迭代对象与迭代器分离的实现
if __name__ == "__main__":
print("========== 可迭代对象与迭代器分离测试 ==========")
# 创建可迭代对象
student_list = StudentList()
# 第一次遍历
print("第一次遍历学生列表:")
for student in student_list:
print(f" {student}")
# 第二次遍历(完全正常,因为每次for循环都会生成新的迭代器)
print("\n第二次遍历学生列表:")
for student in student_list:
print(f" {student}")
# 对比:之前的合并写法,遍历一次后就无法再次遍历
print("\n对比:合并写法的迭代器无法多次遍历")
fib_iter = FibonacciIterator(3)
print("第一次遍历:")
for num in fib_iter:
print(f" {num}")
print("第二次遍历:无任何输出,迭代器已耗尽")
for num in fib_iter:
print(f" {num}")
四、迭代器的核心特性与优缺点
4.1 核心特性1:惰性计算(最核心优势)
惰性计算 :迭代器不会一次性生成所有元素,只有当调用next()函数时,才会生成并返回下一个元素。
核心优势:极大节省内存,即使处理无限大的数据集,也只会占用极小的内存空间。
我们用代码对比列表和迭代器的内存占用差异:
import sys
def test_lazy_compute():
"""测试迭代器的惰性计算与内存优势"""
print("========== 惰性计算与内存占用测试 ==========")
# 1. 列表:一次性生成100万个数字,全部加载到内存
list_nums = [i for i in range(1000000)]
# 查看列表占用的内存大小(字节)
list_memory = sys.getsizeof(list_nums)
print(f"1. 包含100万个元素的列表内存占用:{list_memory / 1024 / 1024:.2f} MB")
# 2. 迭代器:仅生成迭代器对象,不生成任何元素,调用next()时才生成
iter_nums = iter(range(1000000))
# 查看迭代器占用的内存大小(字节)
iter_memory = sys.getsizeof(iter_nums)
print(f"2. 对应迭代器的内存占用:{iter_memory} 字节")
print(f"\n内存差距:{list_memory // iter_memory} 倍以上!")
# 运行测试
test_lazy_compute()
4.2 核心特性2:一次性遍历(单向不可重置)
迭代器只能单向前进、不能后退,遍历完一次之后,迭代器就被"耗尽"了,无法再次遍历,必须重新生成新的迭代器才能再次遍历。
def test_one_time_iter():
"""测试迭代器的一次性遍历特性"""
print("\n========== 一次性遍历特性测试 ==========")
# 1. 列表(可迭代对象):可多次遍历
nums = [1, 2, 3]
print("1. 列表可多次遍历:")
print(f" 第一次遍历:{[i for i in nums]}")
print(f" 第二次遍历:{[i for i in nums]}")
# 2. 迭代器:只能遍历一次,第二次无输出
nums_iter = iter(nums)
print("\n2. 迭代器只能遍历一次:")
print(f" 第一次遍历:{[i for i in nums_iter]}")
print(f" 第二次遍历:{[i for i in nums_iter]}(空列表,迭代器已耗尽)")
# 3. 解决办法:重新生成迭代器
print("\n3. 重新生成迭代器即可再次遍历:")
nums_iter2 = iter(nums)
print(f" 新迭代器遍历:{[i for i in nums_iter2]}")
# 运行测试
test_one_time_iter()
4.3 核心特性3:支持无限序列
因为惰性计算的特性,迭代器可以表示一个无限长的序列,而列表永远无法存储无限多的元素。
class InfiniteNumber:
"""无限递增的数字迭代器"""
def __init__(self, start=0, step=1):
self.current = start
self.step = step
def __iter__(self):
return self
def __next__(self):
return_value = self.current
self.current += self.step
return return_value
# 测试无限迭代器
if __name__ == "__main__":
print("\n========== 无限序列迭代器测试 ==========")
# 创建无限迭代器,从1开始,步长2(奇数序列)
infinite_iter = InfiniteNumber(1, 2)
# 只取前5个元素,不会无限生成
print("无限奇数序列的前5个元素:")
for _ in range(5):
print(f" {next(infinite_iter)}")
# 继续取,会接着之前的位置继续生成
print("\n继续取接下来的3个元素:")
for _ in range(3):
print(f" {next(infinite_iter)}")
4.4 迭代器的优缺点总结
|-------------------------|--------------------|
| 优点 | 缺点 |
| 内存占用极低,惰性计算,适合处理大数据/大文件 | 一次性遍历,无法重复使用,需重新生成 |
| 支持无限序列,突破内存限制 | 无法随机访问元素,只能按顺序逐个获取 |
| 代码解耦,迭代逻辑与业务逻辑分离 | 无法获取长度,不能用len()函数 |
| 可组合使用,构建数据处理流水线 | 遍历过程中无法回退,只能单向前进 |
五、Python内置的可迭代对象与迭代器
5.1 常见的内置可迭代对象
Python中绝大多数容器类型都是可迭代对象,我们可以用iter()将它们转为迭代器,用next()逐个获取元素。
def test_builtin_iterable():
"""测试Python内置的可迭代对象"""
print("========== 内置可迭代对象测试 ==========")
# 1. 字符串
print("1. 字符串迭代器:")
str_iter = iter("python")
print(f" 第一个字符:{next(str_iter)}")
print(f" 第二个字符:{next(str_iter)}")
# 2. 元组
print("\n2. 元组迭代器:")
tuple_iter = iter((10, 20, 30))
print(f" 第一个元素:{next(tuple_iter)}")
# 3. 字典:默认迭代key,也可以迭代value、键值对
print("\n3. 字典迭代器:")
dict1 = {"name": "张三", "age": 20}
# 迭代key
key_iter = iter(dict1)
print(f" 迭代key:{next(key_iter)}")
# 迭代value
value_iter = iter(dict1.values())
print(f" 迭代value:{next(value_iter)}")
# 迭代键值对
item_iter = iter(dict1.items())
print(f" 迭代键值对:{next(item_iter)}")
# 4. 集合
print("\n4. 集合迭代器:")
set_iter = iter({1, 2, 3})
print(f" 第一个元素:{next(set_iter)}")
# 5. range对象
print("\n5. range对象迭代器:")
range_iter = iter(range(3))
print(f" 第一个元素:{next(range_iter)}")
# 6. 文件对象:本身就是迭代器
print("\n6. 文件对象(本身就是迭代器):")
# 打开文件,文件句柄本身就是迭代器,逐行迭代
with open("test.txt", "w", encoding="utf-8") as f:
f.write("第一行\n第二行\n第三行")
with open("test.txt", "r", encoding="utf-8") as f:
print(f" 第一行:{next(f).strip()}")
print(f" 第二行:{next(f).strip()}")
# 运行测试
test_builtin_iterable()
5.2 内置函数返回的迭代器
Python中很多内置函数返回的都是迭代器,而非列表,这是为了节省内存,常用的有map()、filter()、zip()、enumerate()。
def test_builtin_iterator_func():
"""测试返回迭代器的内置函数"""
print("\n========== 内置迭代器函数测试 ==========")
nums = [1, 2, 3, 4, 5]
strs = ["a", "b", "c"]
# 1. map():对可迭代对象的每个元素执行函数,返回迭代器
print("1. map()函数(返回迭代器):")
map_iter = map(lambda x: x * 2, nums)
print(f" map返回的类型:{type(map_iter)}")
print(f" 迭代器内容:{list(map_iter)}")
# 2. filter():过滤符合条件的元素,返回迭代器
print("\n2. filter()函数(返回迭代器):")
filter_iter = filter(lambda x: x % 2 == 0, nums)
print(f" filter返回的类型:{type(filter_iter)}")
print(f" 迭代器内容:{list(filter_iter)}")
# 3. zip():将多个可迭代对象按位置配对,返回迭代器
print("\n3. zip()函数(返回迭代器):")
zip_iter = zip(nums, strs)
print(f" zip返回的类型:{type(zip_iter)}")
print(f" 迭代器内容:{list(zip_iter)}")
# 4. enumerate():为元素添加索引,返回迭代器
print("\n4. enumerate()函数(返回迭代器):")
enum_iter = enumerate(strs, start=1)
print(f" enumerate返回的类型:{type(enum_iter)}")
print(f" 迭代器内容:{list(enum_iter)}")
# 重点:这些函数返回的迭代器,只能遍历一次
print("\n重点:迭代器只能遍历一次")
map_iter2 = map(lambda x: x*2, nums)
print(f" 第一次转列表:{list(map_iter2)}")
print(f" 第二次转列表:{list(map_iter2)}(空列表,已耗尽)")
# 运行测试
test_builtin_iterator_func()
六、迭代器进阶:itertools 高效迭代器工具库
itertools是Python内置的迭代器工具库,提供了大量高效、简洁的迭代器创建函数,专门用于处理迭代操作,所有函数返回的都是迭代器,完美契合惰性计算的特性,是Python迭代器实战的必备工具。
6.1 无限迭代器
生成无限序列的迭代器,需配合终止条件使用,避免无限循环。
import itertools
def test_itertools_infinite():
"""测试itertools无限迭代器"""
print("========== itertools 无限迭代器 ==========")
# 1. count(start, step):从start开始,按step步长无限递增
print("\n1. count() 无限递增序列:")
count_iter = itertools.count(start=1, step=2) # 奇数序列
print(" 前5个元素:", end="")
for _ in range(5):
print(next(count_iter), end=" ")
print()
# 2. cycle(iterable):无限循环遍历可迭代对象的元素
print("\n2. cycle() 无限循环序列:")
cycle_iter = itertools.cycle(["A", "B", "C"])
print(" 前6个元素:", end="")
for _ in range(6):
print(next(cycle_iter), end=" ")
print()
# 3. repeat(obj, times):重复生成指定对象,times不指定则无限重复
print("\n3. repeat() 重复对象:")
repeat_iter = itertools.repeat(10, times=3)
print(" 重复3次10:", list(repeat_iter))
# 运行测试
test_itertools_infinite()
6.2 有限迭代器
常用的有限序列迭代器,用于过滤、切片、拼接、累积等操作。
import itertools
def test_itertools_limited():
"""测试itertools有限迭代器"""
print("\n========== itertools 有限迭代器 ==========")
nums = [1, 2, 3, 4, 5, 6]
strs = ["A", "B", "C", "D"]
# 1. chain(*iterables):拼接多个可迭代对象,返回一个连续的迭代器
print("\n1. chain() 拼接多个可迭代对象:")
chain_iter = itertools.chain(nums, strs)
print(f" 拼接结果:{list(chain_iter)}")
# 2. islice(iterable, start, stop, step):对迭代器切片,不支持负索引
print("\n2. islice() 迭代器切片:")
islice_iter = itertools.islice(nums, 1, 5, 2) # 从索引1到5,步长2
print(f" 切片结果:{list(islice_iter)}")
# 3. accumulate(iterable, func):累积计算,默认累加
print("\n3. accumulate() 累积计算:")
acc_iter = itertools.accumulate(nums)
print(f" 累加结果:{list(acc_iter)}")
# 自定义累积:累乘
acc_mult_iter = itertools.accumulate(nums, lambda x,y: x*y)
print(f" 累乘结果:{list(acc_mult_iter)}")
# 4. takewhile(predicate, iterable):只要条件为True就取元素,条件为False立即停止
print("\n4. takewhile() 按条件取元素:")
take_iter = itertools.takewhile(lambda x: x < 4, nums)
print(f" 小于4的元素:{list(take_iter)}")
# 5. dropwhile(predicate, iterable):丢弃前面符合条件的元素,条件为False开始保留
print("\n5. dropwhile() 按条件丢弃元素:")
drop_iter = itertools.dropwhile(lambda x: x < 4, nums)
print(f" 丢弃小于4的元素后:{list(drop_iter)}")
# 6. filterfalse(predicate, iterable):反向过滤,保留条件为False的元素
print("\n6. filterfalse() 反向过滤:")
filter_iter = itertools.filterfalse(lambda x: x % 2 == 0, nums)
print(f" 保留奇数:{list(filter_iter)}")
# 运行测试
test_itertools_limited()
6.3 组合迭代器
用于生成排列、组合、笛卡尔积等组合序列的迭代器,是算法、数据分析的常用工具。
import itertools
def test_itertools_combine():
"""测试itertools组合迭代器"""
print("\n========== itertools 组合迭代器 ==========")
letters = ["A", "B", "C"]
# 1. product(*iterables, repeat):生成笛卡尔积,替代嵌套for循环
print("\n1. product() 笛卡尔积:")
product_iter = itertools.product(letters, [1, 2])
print(f" 笛卡尔积结果:{list(product_iter)}")
# 2. permutations(iterable, r):生成长度为r的全排列(顺序有关)
print("\n2. permutations() 全排列:")
perm_iter = itertools.permutations(letters, r=2)
print(f" 2个元素的全排列:{list(perm_iter)}")
# 3. combinations(iterable, r):生成长度为r的组合(顺序无关,不重复)
print("\n3. combinations() 组合:")
comb_iter = itertools.combinations(letters, r=2)
print(f" 2个元素的组合:{list(comb_iter)}")
# 4. combinations_with_replacement():允许重复元素的组合
print("\n4. combinations_with_replacement() 可重复组合:")
comb_rep_iter = itertools.combinations_with_replacement(letters, r=2)
print(f" 可重复组合:{list(comb_rep_iter)}")
# 运行测试
test_itertools_combine()
七、迭代器与生成器的区别与联系
很多新手会混淆迭代器和生成器,这里明确两者的核心关系与区别,配有完整代码对比。
7.1 核心关系:生成器是特殊的迭代器
生成器(Generator)是Python提供的一种更简洁的创建迭代器的方式,它完全符合迭代器协议,本质上就是一个迭代器。
- 生成器有两种创建方式:
-
- 生成器表达式:
(i for i in range(10)) - 生成器函数:使用
yield关键字的函数
- 生成器表达式:
7.2 迭代器与生成器的核心区别
|-------|-------------------------------|-----------------------|
| 对比维度 | 迭代器 | 生成器 |
| 创建方式 | 定义类,实现__iter__和__next__方法 | 生成器表达式、带yield的函数 |
| 代码复杂度 | 代码量多,逻辑复杂,需手动维护状态和异常 | 代码简洁,无需手动维护状态和异常 |
| 状态管理 | 需手动在类中定义属性记录遍历位置 | Python自动保存执行状态,无需手动管理 |
| 异常处理 | 需手动抛出StopIteration异常 | 自动处理,函数执行完毕自动抛出异常 |
| 适用场景 | 复杂的迭代逻辑、自定义迭代行为 | 简单的迭代逻辑、快速创建迭代器 |
7.3 代码对比:迭代器 vs 生成器
同样实现一个0-3的数字序列,对比迭代器和生成器的实现差异:
# 方式1:自定义迭代器实现
class NumberIterator:
def __init__(self, end):
self.current = 0
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current >= self.end:
raise StopIteration
return_value = self.current
self.current += 1
return return_value
# 方式2:生成器函数实现
def number_generator(end):
current = 0
while current < end:
# yield关键字:返回当前值,暂停函数执行,下次next()从这里继续
yield current
current += 1
# 方式3:生成器表达式实现
number_gen_expr = (i for i in range(3))
# 测试三者效果完全一致
if __name__ == "__main__":
print("========== 迭代器 vs 生成器 对比测试 ==========")
# 1. 自定义迭代器
print("1. 自定义迭代器:")
iter1 = NumberIterator(3)
for num in iter1:
print(f" {num}", end=" ")
print()
# 2. 生成器函数
print("\n2. 生成器函数:")
gen1 = number_generator(3)
for num in gen1:
print(f" {num}", end=" ")
print()
# 3. 生成器表达式
print("\n3. 生成器表达式:")
gen2 = number_gen_expr
for num in gen2:
print(f" {num}", end=" ")
print()
# 验证生成器本质是迭代器
gen3 = number_generator(3)
print(f"\n生成器是否是迭代器:")
print(f" 有__iter__方法:{hasattr(gen3, '__iter__')}")
print(f" 有__next__方法:{hasattr(gen3, '__next__')}")
print(f" 能用next()调用:{next(gen3)}")
八、新手避坑指南(高频错误+代码对比)
坑1:迭代器遍历耗尽后,无法再次遍历
错误代码:
nums = [1,2,3]
nums_iter = iter(nums)
# 第一次遍历
print("第一次遍历:", list(nums_iter))
# 第二次遍历,无任何输出,迭代器已耗尽
print("第二次遍历:", list(nums_iter))
正确代码:每次需要遍历时,重新生成迭代器
nums = [1,2,3]
# 第一次遍历
nums_iter1 = iter(nums)
print("第一次遍历:", list(nums_iter1))
# 第二次遍历,重新生成迭代器
nums_iter2 = iter(nums)
print("第二次遍历:", list(nums_iter2))
坑2:混淆可迭代对象和迭代器,对可迭代对象调用next()
错误代码:
nums = [1,2,3]
# 列表是可迭代对象,不是迭代器,不能直接用next()
next(nums)
# 报错:TypeError: 'list' object is not an iterator
正确代码:先调用iter()转为迭代器,再用next()
nums = [1,2,3]
nums_iter = iter(nums)
print(next(nums_iter)) # 正常输出1
坑3:自定义迭代器时,__iter__方法没有返回自身
错误代码:
class BadIterator:
def __init__(self):
self.current = 0
def __iter__(self):
# 错误:没有返回迭代器对象,返回了None
print("__iter__被调用了")
def __next__(self):
if self.current >= 3:
raise StopIteration
self.current += 1
return self.current
# 报错:TypeError: iter() returned non-iterator of type 'NoneType'
for num in BadIterator():
print(num)
正确代码:__iter__方法必须返回迭代器自身
class GoodIterator:
def __init__(self):
self.current = 0
def __iter__(self):
# 正确:返回迭代器自身
return self
def __next__(self):
if self.current >= 3:
raise StopIteration
self.current += 1
return self.current
for num in GoodIterator():
print(num)
坑4:自定义迭代器时,没有抛出StopIteration异常,导致无限循环
错误代码:
class InfiniteLoopIterator:
def __init__(self):
self.current = 0
def __iter__(self):
return self
def __next__(self):
# 错误:没有终止条件,不抛StopIteration异常
self.current += 1
return self.current
# 无限循环,永远不会停止
for num in InfiniteLoopIterator():
print(num)
正确代码:必须设置终止条件,无元素时抛出StopIteration
class NormalIterator:
def __init__(self):
self.current = 0
def __iter__(self):
return self
def __next__(self):
# 正确:设置终止条件,抛异常结束迭代
if self.current >= 3:
raise StopIteration
self.current += 1
return self.current
for num in NormalIterator():
print(num)
坑5:对迭代器使用len()函数获取长度
错误代码:
nums_iter = iter([1,2,3])
# 迭代器不支持len(),因为元素是惰性生成的,无法提前知道长度
print(len(nums_iter))
# 报错:TypeError: object of type 'list_iterator' has no len()
正确代码:如果需要获取长度,先转为列表,或提前在可迭代对象中定义长度
nums_iter = iter([1,2,3])
# 转为列表后获取长度(会耗尽迭代器)
nums_list = list(nums_iter)
print(len(nums_list))
九、实战案例(完整可运行代码)
9.1 实战1:大文件逐行读取(迭代器内存优势)
处理GB级别的大文件时,用readlines()会一次性把整个文件加载到内存,导致内存溢出;用文件迭代器逐行读取,内存占用始终极低。
def read_large_file(file_path):
"""
用迭代器逐行读取大文件,内存占用极低
:param file_path: 文件路径
"""
print("========== 大文件逐行读取实战 ==========")
line_count = 0
# 文件句柄本身就是迭代器,逐行迭代,不会一次性加载整个文件
with open(file_path, "r", encoding="utf-8") as f:
# for循环自动调用next(),逐行读取
for line in f:
# 处理每一行数据
line = line.strip()
if line: # 跳过空行
line_count += 1
# 这里可以添加你的业务处理逻辑
if line_count <= 5: # 只打印前5行
print(f" 第{line_count}行:{line}")
print(f"\n文件总行数:{line_count}")
print(f"内存占用:仅当前行的内容,不会加载整个文件")
# 测试:先生成一个测试大文件,再读取
if __name__ == "__main__":
# 生成测试文件
with open("large_test.log", "w", encoding="utf-8") as f:
for i in range(10000):
f.write(f"这是第{i}行日志,记录了系统运行状态\n")
# 读取大文件
read_large_file("large_test.log")
9.2 实战2:自定义分页数据迭代器
在爬虫、接口请求等场景中,数据是分页返回的,用迭代器实现惰性加载,无需一次性请求所有分页数据,用的时候才请求下一页。
import requests
class PageDataIterator:
"""分页数据迭代器,惰性请求分页接口"""
def __init__(self, base_url, max_page=10):
"""
初始化
:param base_url: 接口基础地址
:param max_page: 最大请求页数
"""
self.base_url = base_url
self.max_page = max_page
self.current_page = 1 # 当前页码
def __iter__(self):
return self
def __next__(self):
# 终止条件:超过最大页码
if self.current_page > self.max_page:
raise StopIteration
# 模拟请求接口,获取当前页数据(实战中替换为真实接口请求)
print(f"正在请求第{self.current_page}页数据...")
# 真实场景:response = requests.get(self.base_url, params={"page": self.current_page})
# data = response.json()
# 模拟返回数据
data = {
"page": self.current_page,
"data": [f"数据{i}" for i in range((self.current_page-1)*10, self.current_page*10)]
}
# 页码+1,为下一次请求做准备
self.current_page += 1
# 返回当前页数据
return data
# 测试分页迭代器
if __name__ == "__main__":
print("========== 分页数据迭代器实战 ==========")
# 创建分页迭代器,最多请求3页
page_iter = PageDataIterator("https://api.example.com/data", max_page=3)
# 遍历迭代器,自动逐页请求数据
for page_data in page_iter:
print(f"第{page_data['page']}页数据:{page_data['data'][:3]}...(共10条)")
9.3 实战3:数据处理流水线(迭代器组合使用)
用多个迭代器组合成数据处理流水线,惰性计算,每一步只处理当前元素,内存占用极低,适合大数据处理。
import itertools
def data_process_pipeline():
"""迭代器组合的数据处理流水线"""
print("========== 数据处理流水线实战 ==========")
# 原始数据:1-10的数字
nums = range(1, 11)
print(f"原始数据:{list(nums)}")
# 构建处理流水线(全是迭代器,不执行实际计算,调用next()时才执行)
# 步骤1:过滤出偶数
step1 = filter(lambda x: x % 2 == 0, nums)
# 步骤2:每个数乘以2
step2 = map(lambda x: x * 2, step1)
# 步骤3:累加计算
step3 = itertools.accumulate(step2)
# 步骤4:过滤出大于20的数
step4 = itertools.dropwhile(lambda x: x < 20, step3)
# 执行流水线,获取最终结果
print(f"处理结果:{list(step4)}")
print("处理流程:过滤偶数 → 乘以2 → 累加 → 过滤大于20的数")
# 运行测试
data_process_pipeline()
十、迭代器核心知识点速查表
|------------|----------------------------------------------------------|
| 知识点 | 核心说明 |
| 可迭代对象 | 实现了__iter__()方法的对象,能被for循环遍历 |
| 迭代器 | 同时实现了__iter__()和__next__()方法的对象,符合迭代器协议 |
| iter()函数 | 将可迭代对象转为迭代器 |
| next()函数 | 获取迭代器的下一个元素,无元素时抛StopIteration异常 |
| for循环底层 | 先调用iter()获取迭代器,循环调用next(),捕获StopIteration结束 |
| 核心特性 | 惰性计算、一次性遍历、内存高效、支持无限序列 |
| 自定义迭代器 | 实现__iter__()(返回self)和__next__()(返回元素,无元素抛异常) |
| 内置迭代器函数 | map、filter、zip、enumerate,返回值均为迭代器 |
| itertools库 | Python内置的高效迭代器工具库,提供大量迭代器创建函数 |
| 生成器 | 特殊的迭代器,用yield关键字或生成器表达式创建,代码更简洁 |
| 常见坑 | 迭代器耗尽无法重复遍历、可迭代对象不能直接用next()、__iter__不返回self、无终止条件导致无限循环 |