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