生成器是 Python 中的特殊可迭代对象,它们允许按需创建序列,而无需同时占用整个集合的内存。它们通过带有关键字 yield 的函数或生成器表达式 ((expr for ... in ...)) 实现。在处理大量数据或潜在的无限流时,这种方法非常方便。
与列表表达式的关键区别:
[x for x in range(10)]) 会立即在内存中创建整个列表。(x for x in range(10))) 会逐个创建元素,消耗的内存要少得多。何时使用生成器:
# 生成器函数 def counter(n): for i in range(n): yield i for number in counter(5): print(number)
"使用带有 yield 的函数和普通返回列表的函数有什么区别?请举例说明。"
答案:
普通函数立即计算并返回列表,占用所有元素的内存。带有 yield 的函数返回生成器,逐个返回元素,并且不会立即加载整个序列到内存中。
def make_list(n): return [i for i in range(n)] # 立即返回列表,占用大量内存 def make_generator(n): for i in range(n): yield i # 将逐个返回元素
故事
在一个分析大型日志的项目中,使用列表表达式提取包含错误的行:
error_lines = [line for line in open('biglog.txt') if 'ERROR' in line]
文件超过 2 GB,应用程序由于 OOM(内存溢出)崩溃。应该使用生成器:
error_lines = (line for line in open('biglog.txt') if 'ERROR' in line)
故事
一名员工想分析一个短列表,编写了一个带有 yield 的函数,但忘记了返回的是生成器,而不是列表:
result = my_generator_function() # result 是生成器,而不是列表 if len(result) > 5: # TypeError: object of type 'generator' has no len()
修正方法:将结果包裹在 list() 中。
故事
尝试多次迭代生成器:
numbers = (i for i in range(5)) for n in numbers: pass # 耗尽了生成器 for n in numbers: print(n) # 不输出任何内容
生成器是一次性的。要重复使用,需要创建一个新的生成器。