510 lines
14 KiB
Python
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']
|
|
|
|
# 优势:
|
|
|
|
# 惰性求值,内存效率高
|
|
# 函数式编程风格
|
|
# 易于组合复杂的数据处理逻辑
|
|
# 适合处理大数据集
|
|
|
|
|
|
# 迭代器性能优势
|
|
# 内存效率:
|
|
|
|
# 列表:一次性创建所有元素,占用大量内存
|
|
# 迭代器:按需生成元素,内存占用极小
|
|
# 处理速度:
|
|
|
|
# 列表:需要预先计算所有元素
|
|
# 迭代器:惰性求值,只计算需要的元素
|
|
# 适用场景:
|
|
|
|
# 大数据集处理
|
|
# 无限数据流
|
|
# 内存受限环境
|
|
# 流式数据处理
|