目录一、引言内存效率为何重要二、迭代器Iterator2.1 可迭代对象与迭代器协议2.2 手动实现迭代器2.3 内置迭代器工具三、生成器Generator3.1 生成器函数yield的魔力3.2 生成器表达式3.3 生成器的惰性求值机制四、内存效率对比实战4.1 列表 vs 生成器内存占用4.2 处理大文件时的优势4.3 无限序列的表示五、生成器的高级特性5.1 send()、throw()和close()5.2 使用yield from简化嵌套生成器六、迭代器与生成器的应用场景6.1 数据流处理管道6.2 斐波那契数列的生成6.3 分块读取大型数据集七、常见问题与面试考点八、总结一、引言内存效率为何重要在Python编程中我们经常需要处理大量数据。如果一次性将所有数据加载到内存中例如使用列表当数据量达到百万、千万级别时内存占用会急剧上升甚至导致程序崩溃。传统做法使用列表存储所有中间结果。问题内存浪费无法处理无限序列程序响应变慢。迭代器与生成器提供了一种惰性求值Lazy Evaluation的解决方案它们不预先计算所有元素而是按需生成每次只产生一个值。这使得程序的内存占用大幅降低且能表示无限序列。本文将从原理到实践带你彻底掌握这两个提升内存效率的利器。二、迭代器Iterator2.1 可迭代对象与迭代器协议可迭代对象Iterable实现了__iter__()方法的对象该方法返回一个迭代器。例如list、tuple、str、dict、set。迭代器Iterator实现了__iter__()和__next__()方法的对象。__next__()返回下一个元素当没有元素时抛出StopIteration异常。迭代器协议任何实现了__iter__()和__next__()的类都是迭代器。python # 判断是否可迭代/迭代器 from collections.abc import Iterable, Iterator print(isinstance([1,2,3], Iterable)) # True print(isinstance([1,2,3], Iterator)) # False it iter([1,2,3]) # 获取迭代器 print(isinstance(it, Iterator)) # True # 运行结果 True False True2.2 手动实现迭代器我们可以通过定义一个类实现__iter__()和__next__()来创建自定义迭代器。python 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 for num in CountDown(5): print(num, end ) # 运行结果 5 4 3 2 12.3 内置迭代器工具Python提供了iter()和next()函数来操作迭代器。for循环底层也是通过迭代器实现的。python nums [10, 20, 30] it iter(nums) print(next(it)) # 10 print(next(it)) # 20 print(next(it)) # 30 # print(next(it)) # 抛出 StopIteration # 运行结果 10 20 30三、生成器Generator生成器是一种特殊的迭代器它比手动实现迭代器类要简洁得多。生成器有两种形式生成器函数和生成器表达式。3.1 生成器函数yield的魔力在函数中使用yield关键字代替return该函数就变成了生成器函数。调用生成器函数返回一个生成器对象**该对象自动实现迭代器协议。python def count_down(n): while n 0: yield n n - 1 gen count_down(5) print(type(gen)) # class generator print(list(gen)) # 转换为列表 # 运行结果 class generator [5, 4, 3, 2, 1]关键特性每次调用next(gen)函数执行到yield处暂停并返回值下次调用从暂停处继续。函数状态局部变量会被保留。当函数返回或抛出StopIteration时迭代结束。3.2 生成器表达式生成器表达式类似于列表推导式但使用圆括号()而不是方括号[]。它返回生成器对象而不是一次性构建列表。python # 列表推导式立即生成所有数据 list_comp [x**2 for x in range(10)] # 生成器表达式惰性生成 gen_exp (x**2 for x in range(10)) print(type(list_comp)) # class list print(type(gen_exp)) # class generator print(sum(gen_exp)) # 014...81 285 # 运行结果 class list class generator 2853.3 生成器的惰性求值机制生成器不会一次性计算所有元素而是仅在请求时才生成下一个值。这种按需计算的方式称为惰性求值。python def infinite_sequence(): num 0 while True: yield num num 1 gen infinite_sequence() print(next(gen)) # 0 print(next(gen)) # 1 print(next(gen)) # 2 # 可以无限调用不会内存溢出 # 运行结果 0 1 2四、内存效率对比实战4.1 列表 vs 生成器内存占用下面通过sys.getsizeof()比较列表和生成器的内存占用。python import sys # 列表一次性存储100万个整数 list_data [i for i in range(1_000_000)] # 生成器惰性产生整数 gen_data (i for i in range(1_000_000)) print(f列表内存占用{sys.getsizeof(list_data)} 字节) print(f生成器内存占用{sys.getsizeof(gen_data)} 字节) # 运行结果示例 列表内存占用8000056 字节 生成器内存占用112 字节生成器只占用了极小的固定内存而列表随着元素增多线性增长。4.2 处理大文件时的优势假设有一个10GB的日志文件我们需要统计包含ERROR的行数。使用列表读取所有行会内存爆炸而生成器可以逐行处理。python def count_errors(filepath): count 0 with open(filepath, r, encodingutf-8) as f: for line in f: # 文件对象本身是可迭代的生成器 if ERROR in line: count 1 return count # 调用示例假设文件存在 # errors count_errors(huge_log.txt) # print(errors)说明open()返回的文件对象是逐行读取的迭代器每次只将一行读入内存适合处理任意大小的文件。4.3 无限序列的表示列表无法表示无限序列而生成器可以轻松实现。python def fibonacci(): a, b 0, 1 while True: yield a a, b b, a b fib fibonacci() for _ in range(10): print(next(fib), end ) # 运行结果 0 1 1 2 3 5 8 13 21 34五、生成器的高级特性5.1 send()、throw()和close()生成器不仅可以通过next()获取值还可以通过send(value)向生成器内部发送值throw()向内部抛出异常close()关闭生成器。python def echo(): while True: received yield print(f收到{received}) g echo() next(g) # 预激生成器执行到yield暂停 g.send(Hello) g.send(World) g.close() # g.send(Again) # 关闭后调用会抛出 StopIteration # 运行结果 收到Hello 收到World注意首次调用生成器时必须先执行next(g)或g.send(None)将执行推进到第一个yield处才能使用send发送值。5.2 使用yield from简化嵌套生成器yield from可以将另一个生成器或可迭代对象的所有值逐个产出简化嵌套循环。python def chain(*iterables): for it in iterables: yield from it # 等价于 for x in it: yield x combined chain([1,2,3], ABC, range(2)) print(list(combined)) # 运行结果 [1, 2, 3, A, B, C, 0, 1]六、迭代器与生成器的应用场景6.1 数据流处理管道生成器可以像Unix管道一样串联每个环节只处理当前元素。python def read_file(filepath): with open(filepath) as f: for line in f: yield line.strip() def filter_errors(lines): for line in lines: if ERROR in line: yield line def extract_code(lines): for line in lines: # 假设错误格式包含代码如 [ERR-1234] start line.find([ERR-) if start ! -1: end line.find(], start) yield line[start1:end] # 管道处理 pipeline extract_code(filter_errors(read_file(app.log))) for code in pipeline: print(code)6.2 斐波那契数列的生成python def fib_generator(limitNone): a, b 0, 1 count 0 while limit is None or count limit: yield a a, b b, a b count 1 # 获取前15个斐波那契数 print(list(fib_generator(15))) # 运行结果 [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]6.3 分块读取大型数据集处理数据库查询结果或API分页时生成器可以按批次获取数据。python def paginated_fetch(page_size): page 1 while True: # 模拟从API获取一页数据 data fetch_from_api(page, page_size) # 自定义函数 if not data: break yield data page 1 # 逐页处理 for page_data in paginated_fetch(100): process_page(page_data) # 每次只处理一页内存可控七、常见问题与面试考点问迭代器和可迭代对象的区别答可迭代对象实现了__iter__()返回迭代器迭代器实现了__iter__()和__next__()。迭代器只能遍历一次可迭代对象可以重复获取新迭代器。问生成器是迭代器吗答是的生成器是迭代器的一种更简洁的实现方式。生成器对象同时拥有__iter__()和__next__()方法。问yield与return的区别答return结束函数并返回值yield暂停函数并返回值下次调用next()时从暂停点继续。一个生成器函数可以有多个yield。问生成器表达式和列表推导式哪个更快答生成器表达式创建时几乎没有开销但遍历时每个元素需要实时计算列表推导式一次性计算所有元素创建较慢但遍历更快。内存敏感场景选生成器速度优先且数据量小时可选列表。问如何判断一个对象是生成器答可以使用isinstance(obj, types.GeneratorType)需要import types或检查对象是否有send、throw等生成器特有的方法。问生成器能重复使用吗答不能。生成器是一次性的遍历完后会耗尽。如果需要重新遍历请重新调用生成器函数或创建新的生成器对象。八、总结迭代器是遵循迭代器协议的对象通过__iter__()和__next__()实现按需取值。生成器是迭代器的简化形式使用yield关键字编写生成器函数或使用生成器表达式。惰性求值使得生成器在处理大数据、无限序列、流式数据时具有极低的内存占用。实际开发中应优先考虑使用生成器代替大型列表尤其适合文件处理、数据管道、分页加载等场景。