Files
python/20-24/04deidaiqi.py
2026-05-06 11:21:42 +08:00

510 lines
14 KiB
Python

# 迭代器是 Python 中用于遍历数据集合的工具,它提供了一种统一、高效的方式来访问各种数据结构中的元素。
# 1.1.什么是迭代器?
# 迭代器是一个实现了迭代协议的对象,具有以下特征:
# 迭代协议:必须实现 __iter__() 和 __next__() 方法
# __iter__():返回迭代器对象本身,使对象可以被 iter() 调用
# __next__():返回下一个元素,如果没有更多元素则抛出 StopIteration 异常
# 惰性求值:按需生成元素,不预计算所有值,节省内存
# 状态保持:记住当前的遍历位置
# 单向遍历:只能向前遍历,不能后退或重复
# 迭代器 vs 可迭代对象
# 理解迭代器和可迭代对象的区别很重要:
# 可迭代对象 (Iterable):可以被迭代的对象,如列表、元组、字符串等
# 迭代器 (Iterator):实际执行迭代的对象,实现了 __next__() 方法
# 重要说明:
# 可迭代对象可以通过 iter() 函数转换为迭代器
# 迭代器只能使用一次,遍历完后需要重新创建
# 大多数内置容器类型(如 list、tuple、dict)都是可迭代对象,但不是迭代器
from collections.abc import Iterable, Iterator
# 列表是可迭代对象,但不是迭代器
numbers = [1, 2, 3]
print(isinstance(numbers, Iterable)) # True
print(isinstance(numbers, Iterator)) # False
# 通过 iter() 获取迭代器
numbers_iterator = iter(numbers)
print(isinstance(numbers_iterator, Iterator)) # True
# 迭代器协议
# 迭代器协议是 Python 中定义迭代器行为的标准,任何对象只要实现了这个协议,就可以被用于迭代。
# 2.1.必须实现的方法
# 要成为一个合格的迭代器,必须实现以下两个方法:
# __iter__() 方法
# 返回迭代器对象自身
# 使对象可以被 for 循环或 iter() 调用
# 通常实现为 return self
# __next__() 方法
# 返回下一个元素
# 如果没有更多元素,必须抛出 StopIteration 异常
# 这是迭代器的核心逻辑
def __iter__(self):
return self
def __next__(self):
# 实现具体的迭代逻辑
# 如果没有更多元素,抛出 StopIteration
pass
# 协议特点:
# 任何实现了这两个方法的对象都可以用于 for 循环
# 支持 next() 函数调用
# 可以与 enumerate()、zip() 等内置函数配合使用
# 完整示例
class MyIterator:
"""自定义迭代器示例"""
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
"""返回迭代器自身"""
return self
def __next__(self):
"""返回下一个元素"""
if self.index >= len(self.data):
raise StopIteration
value = self.data[self.index]
self.index += 1
return value
# 使用自定义迭代器
my_iter = MyIterator([1, 2, 3])
for item in my_iter:
print(item) # 输出: 1, 2, 3
# 也可以手动调用 next()
my_iter2 = MyIterator(["a", "b", "c"])
print(next(my_iter2)) # 'a'
print(next(my_iter2)) # 'b'
print(next(my_iter2)) # 'c'
# print(next(my_iter2)) # 抛出 StopIteration 异常
# 3.创建迭代器的方法
# 3.1.方法1:实现迭代器类
# 通过定义一个类并实现迭代器协议来创建自定义迭代器:
class Countdown:
"""倒计时迭代器"""
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
value = self.current
self.current -= 1
return value
# 使用倒计时迭代器
countdown = Countdown(5)
for number in countdown:
print(number, end=" ") # 输出: 5 4 3 2 1
# 适用场景:
# 需要复杂的状态管理
# 需要实现特殊的迭代逻辑
# 需要封装复杂的迭代行为
# 方法2:使用生成器函数
# 生成器是一种特殊的迭代器,使用 yield 关键字创建,代码更简洁且内存效率更高:
def simple_gen():
"""简单的生成器函数"""
print("First yield")
yield 1
print("Second yield")
yield 2
print("Done")
# 创建生成器对象
g = simple_gen()
print(next(g)) # 输出: First yield \n 1
print(next(g)) # 输出: Second yield \n 2
# print(next(g)) # 会引发 StopIteration 异常
# 生成器可以用 for 循环遍历
for value in simple_gen():
print(f"Got: {value}")
# 生成器的优势:
# 代码更简洁,不需要实现 __iter__() 和 __next__() 方法
# 自动管理状态,无需手动维护索引
# 内存效率高,按需生成值
# 支持复杂的控制流(如异常处理、资源管理)
# 方法3:使用生成器表达式
# 生成器表达式是创建迭代器最简洁的方式,语法类似列表推导式,但使用圆括号:
# 基本语法
# (expression for item in iterable [if condition])
# 创建平方数生成器
squares = (x**2 for x in range(5))
print(list(squares)) # [0, 1, 4, 9, 16]
# 带条件的生成器表达式
even_squares = (x**2 for x in range(10) if x % 2 == 0)
print(list(even_squares)) # [0, 4, 16, 36, 64]
# 手动迭代
gen = (x * 2 for x in range(3))
print(next(gen)) # 0
print(next(gen)) # 2
print(next(gen)) # 4
# print(next(gen)) # StopIteration
# 生成器表达式的优势:
# 语法简洁,一行代码创建迭代器
# 内存效率高,惰性求值
# 适合简单的数据转换和过滤
# 可以与其他迭代器函数链式组合
# 内置迭代器工具
# Python 提供了许多内置函数来操作迭代器,这些工具让迭代操作更加灵活和高效。
# 4.1.iter() 和 next() 函数
# 这两个函数是操作迭代器的基础工具:
# iter(iterable):将可迭代对象转换为迭代器
# next(iterator):获取迭代器的下一个元素
# next(iterator, default):获取下一个元素,如果迭代结束则返回默认值
# 创建迭代器
numbers = [1, 2, 3, 4, 5]
iterator = iter(numbers)
# 手动获取元素
print(next(iterator)) # 1
print(next(iterator)) # 2
print(next(iterator)) # 3
print(next(iterator)) # 4
print(next(iterator)) # 5
# print(next(iterator)) # 抛出 StopIteration 异常
# 使用默认值避免异常
iterator2 = iter([1, 2])
print(next(iterator2)) # 1
print(next(iterator2)) # 2
print(next(iterator2, "没有更多元素")) # 没有更多元素
# 使用场景:
# 需要精确控制迭代过程
# 处理大型数据集时避免一次性加载
# 实现自定义的迭代逻辑
# enumerate() - 带索引的迭代
# enumerate() 函数在遍历可迭代对象时同时提供索引和值,是处理需要索引的场景的最佳选择。
# 语法
# enumerate(iterable, start=0)
# 参数:
# iterable:要遍历的可迭代对象
# start:索引起始值,默认为 0
# 返回值:返回 (索引, 元素) 元组的迭代器
# 基本用法
colors = ["red", "green", "blue"]
for idx, color in enumerate(colors):
print(f"{idx}: {color}")
# 输出:
# 0: red
# 1: green
# 2: blue
# 自定义起始索引
for idx, color in enumerate(colors, start=1):
print(f"{idx}个颜色: {color}")
# 输出:
# 第1个颜色: red
# 第2个颜色: green
# 第3个颜色: blue
# 常见应用场景:
# 需要同时访问索引和值的循环
# 创建带编号的输出
# 在循环中修改列表元素
# 生成带索引的数据结构
# zip() - 并行迭代
# zip() 函数将多个可迭代对象"并行"组合,每次迭代返回一个包含各对象对应元素的元组。
# 语法:
# zip(iter1, iter2, ..., iterN)
# 特点:
# 返回迭代器,惰性求值
# 以最短的可迭代对象长度为准
# 支持任意数量的可迭代对象
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
cities = ["北京", "上海", "广州"]
for name, age, city in zip(names, ages, cities):
print(f"{name},{age}岁,来自{city}")
# 长度不同:
# 以最短的为准
list1 = [1, 2, 3, 4]
list2 = ["a", "b"]
for num, letter in zip(list1, list2):
print(f"{num}: {letter}")
# 输出:
# 1: a
# 2: b
# 高级用法:
# 数据转置
# 行转列
scores = [
(85, 92, 78), # 张三的成绩
(76, 88, 95), # 李四的成绩
(90, 85, 92), # 王五的成绩
]
chinese, math, english = zip(*scores)
print("语文成绩:", chinese) # (85, 76, 90)
print("数学成绩:", math) # (92, 88, 85)
print("英语成绩:", english) # (78, 95, 92)
# 与推导式结合:
# 计算对应位置元素的和
list1 = [1, 2, 3]
list2 = [4, 5, 6]
result = [x + y for x, y in zip(list1, list2)]
print(result) # [5, 7, 9]
# 处理不同长度的序列:
from itertools import zip_longest
# 保留所有元素,用默认值填充
a = [1, 2, 3]
b = [4, 5]
for x, y in zip_longest(a, b, fillvalue=0):
print(f"{x} + {y} = {x + y}")
# 输出:
# 1 + 4 = 5
# 2 + 5 = 7
# 3 + 0 = 3
# map() - 映射迭代
# map() 函数对可迭代对象的每个元素应用指定函数,返回一个迭代器。
# 语法:
# map(function, iterable, ...)
# 参数:
# function:要应用的函数(可以是内置函数、lambda 或自定义函数)
# iterable:一个或多个可迭代对象
# 特点:
# 返回惰性迭代器,需要显式消费
# 支持多个可迭代对象,以最短的为准
# 适合批量数据转换
# 基本用法
numbers = [1, 2, 3, 4]
squared = map(lambda x: x**2, numbers)
print(list(squared)) # [1, 4, 9, 16]
# 类型转换
nums = [10, 20, 30]
str_list = map(str, nums)
print(list(str_list)) # ['10', '20', '30']
# 多序列处理
list1 = [1, 2, 3]
list2 = [4, 5, 6, 7]
result = map(lambda x, y: x + y, list1, list2)
print(list(result)) # [5, 7, 9]
# 自定义函数
def double(x):
return x * 2
numbers = [5, 6, 7]
doubled = map(double, numbers)
print(list(doubled)) # [10, 12, 14]
# 优势:
# 代码简洁,函数式编程风格
# 惰性求值,内存效率高
# 易于与其他迭代器函数组合
# filter() - 过滤迭代
# filter() 函数根据指定条件过滤可迭代对象中的元素,只保留满足条件的元素。
# 语法:
# filter(function, iterable)
# 参数:
# function:判断函数,返回 True/False。如果为 None,则使用元素自身的布尔值
# iterable:要过滤的可迭代对象
# 特点:
# 返回惰性迭代器,需要显式消费
# 只保留使函数返回 True 的元素
# 适合数据清洗和条件筛选
# 基本用法
numbers = [1, 2, 3, 4, 5, 6]
even_numbers = filter(lambda x: x % 2 == 0, numbers)
print(list(even_numbers)) # [2, 4, 6]
# 过滤假值
data = ["hello", "", "python", None, " ", "AI"]
valid_data = filter(None, data)
print(list(valid_data)) # ['hello', 'python', ' ', 'AI']
# 自定义过滤函数
def is_positive(x):
return x > 0
numbers = [0, -1, 2, -3, 4]
positive = filter(is_positive, numbers)
print(list(positive)) # [2, 4]
# 复杂条件
def is_valid_email(email):
return email and "@" in email and "." in email
emails = ["user@example.com", "invalid", "test@domain.org", ""]
valid_emails = filter(is_valid_email, emails)
print(list(valid_emails)) # ['user@example.com', 'test@domain.org']
# 应用场景:
# 数据清洗和验证
# 条件筛选
# 去除无效数据
# 与 map() 等函数组合使用
# 迭代器链式操作
# 链式操作将多个迭代器函数串联起来,形成数据处理流水线,具有惰性求值优势。
# 1.常见链式操作函数
# filter(func, iterable):过滤满足条件的元素
# map(func, iterable):对每个元素应用函数
# itertools.islice(iterable, n):取前n个元素
import itertools
def process_data_pipeline(data):
"""数据处理流水线"""
# 1. 过滤正数
filtered = filter(lambda x: x > 0, data)
# 2. 计算平方
squared = map(lambda x: x**2, filtered)
# 3. 取前5个
limited = itertools.islice(squared, 5)
return limited
# 示例数据
data = [-3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
result = process_data_pipeline(data)
print(list(result)) # [1, 4, 9, 16, 25]
# 更简洁的写法
result = itertools.islice(map(lambda x: x**2, filter(lambda x: x > 0, data)), 5)
print(list(result)) # [1, 4, 9, 16, 25]
# 优势:
# 内存效率:按需处理,不一次性加载所有数据
# 性能优化:只为需要的数据执行运算
# 代码简洁:易于组合复杂的数据处理逻辑
# 可读性强:流水线式的处理流程清晰明了
# 应用场景:
# 数据清洗和预处理
# 日志文件分析
# 流式数据处理
# 大数据集处理
# 迭代器工具函数
# 1.itertools 常用函数
# itertools 模块提供了丰富的迭代器构造工具,支持流式数据处理:
# chain(*iterables):连接多个可迭代对象
# compress(data, selectors):根据布尔掩码选择元素
# dropwhile(predicate, iterable):丢弃开头满足条件的元素
# takewhile(predicate, iterable):获取开头满足条件的元素
# groupby(iterable, key=None):按key分组(需要先排序)
import itertools
# chain - 连接多个迭代器
chain_iter = itertools.chain([1, 2], [3, 4], [5, 6])
print("Chain:", list(chain_iter)) # [1, 2, 3, 4, 5, 6]
# compress - 条件过滤
data = ["A", "B", "C", "D"]
selectors = [1, 0, 1, 0]
compress_iter = itertools.compress(data, selectors)
print("Compress:", list(compress_iter)) # ['A', 'C']
# dropwhile - 丢弃开头满足条件的元素
drop_iter = itertools.dropwhile(lambda x: x < 5, [1, 2, 3, 4, 5, 6, 1, 2])
print("Dropwhile:", list(drop_iter)) # [5, 6, 1, 2]
# takewhile - 获取开头满足条件的元素
take_iter = itertools.takewhile(lambda x: x < 5, [1, 2, 3, 4, 5, 6, 1, 2])
print("Takewhile:", list(take_iter)) # [1, 2, 3, 4]
# groupby - 分组(需要先排序)
data = ["apple", "animal", "banana", "bird", "cherry", "cat"]
sorted_data = sorted(data, key=lambda x: x[0]) # 按首字母排序
grouped = itertools.groupby(sorted_data, key=lambda x: x[0])
for key, group in grouped:
print(f"{key}: {list(group)}")
# 输出:
# a: ['animal', 'apple']
# b: ['banana', 'bird']
# c: ['cat', 'cherry']
# 优势:
# 惰性求值,内存效率高
# 函数式编程风格
# 易于组合复杂的数据处理逻辑
# 适合处理大数据集
# 迭代器性能优势
# 内存效率:
# 列表:一次性创建所有元素,占用大量内存
# 迭代器:按需生成元素,内存占用极小
# 处理速度:
# 列表:需要预先计算所有元素
# 迭代器:惰性求值,只计算需要的元素
# 适用场景:
# 大数据集处理
# 无限数据流
# 内存受限环境
# 流式数据处理