欢迎光临
我们一直在努力

Python 迭代器与生成器:惰性计算与内存优化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

1. 引言

在处理大数据、读取超大文件或构造无限序列时,一次性把所有数据加载进内存往往不可行,甚至直接导致程序崩溃。Python 的迭代器(Iterator)与生成器(Generator)正是为解决这类问题而生:它们采用惰性计算(Lazy Evaluation),按需逐个产出元素,从而大幅降低内存占用。

本文将从可迭代对象与迭代器协议讲起,深入剖析 iter() / next() 的运作机制,再介绍 yield 生成器与生成器表达式,最后通过大文件读取与无限序列两个实战案例,展示惰性计算如何优化内存。

2. 可迭代对象与迭代器协议

2.1 什么是可迭代对象

可迭代对象(Iterable) 是实现了 __iter__() 方法的对象,可以被 for 循环遍历。常见的可迭代对象包括:

  • list、tuple、str
  • dict、set
  • 文件对象
  • 自定义类(实现了 __iter__())

lst = [1, 2, 3]
print(hasattr(lst, "__iter__")) # True,list 是可迭代对象

2.2 迭代器协议

迭代器(Iterator) 是实现了迭代器协议的对象,需要同时具备两个方法:

  • __iter__():返回迭代器自身。
  • __next__():返回下一个元素;没有更多元素时抛出 StopIteration 异常。

class CountDown:
def __init__(self, n):
self.n = n

def __iter__(self):
return self

def __next__(self):
if self.n <= 0:
raise StopIteration
self.n -= 1
return self.n + 1

for x in CountDown(3):
print(x) # 依次输出 3、2、1

2.3 可迭代对象 vs 迭代器

概念是否可 for 遍历是否消耗元素典型例子
可迭代对象 是 否,可重复遍历 list、str
迭代器 是 是,遍历后耗尽 iter(list)、生成器

lst = [1, 2, 3]
it = iter(lst) # 把可迭代对象转成迭代器
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# print(next(it)) # 报错:StopIteration

3. iter 与 next 详解

3.1 iter() 函数

iter() 用于把可迭代对象转换为迭代器:

s = "hello"
it = iter(s)
print(next(it)) # h
print(next(it)) # e

iter() 也可以配合哨兵值使用,用于反复调用某个函数直到返回哨兵值:

def read_line():
return input("输入(输入 end 结束):")

for line in iter(read_line, "end"):
print("收到:", line)

3.2 next() 函数

next() 用于获取迭代器的下一个元素,可传入默认值避免 StopIteration:

it = iter([1, 2])
print(next(it, "结束")) # 1
print(next(it, "结束")) # 2
print(next(it, "结束")) # 结束,不抛异常

3.3 for 循环的本质

for 循环本质上就是 iter() + next() 的语法糖:

it = iter([1, 2, 3])
while True:
try:
x = next(it)
print(x)
except StopIteration:
break

4. yield 生成器

4.1 什么是生成器

生成器(Generator) 是包含 yield 关键字的函数。调用生成器函数不会立即执行函数体,而是返回一个生成器对象(迭代器)。每次调用 next() 时,函数执行到 yield 处暂停,并产出值;下次调用时从暂停处继续。

def count_up_to(n):
i = 1
while i <= n:
yield i
i += 1

gen = count_up_to(3)
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3

4.2 惰性计算的威力

生成器最大的价值在于惰性计算:元素按需生成,不一次性全部创建。

def squares(n):
for i in range(n):
yield i * i

# 生成器逐个产出,内存占用恒定
for s in squares(1000000):
if s > 100:
break
print(s) # 只计算到 100 就停止

4.3 yield 与 return 的区别

  • yield:暂停函数,产出值,可多次使用。
  • return:结束函数,返回最终结果。

def gen_demo():
yield 1
yield 2
return "done" # 结束,返回值不会作为元素产出

g = gen_demo()
print(next(g)) # 1
print(next(g)) # 2
# print(next(g)) # 报错:StopIteration

5. 生成器表达式

5.1 语法

生成器表达式与列表推导式语法相似,但使用圆括号,且惰性求值:

# 列表推导式:一次性生成全部元素
squares_list = [x * x for x in range(10)]

# 生成器表达式:惰性生成
squares_gen = (x * x for x in range(10))

print(type(squares_list)) # <class 'list'>
print(type(squares_gen)) # <class 'generator'>

5.2 内存对比

import sys

list_comp = [x for x in range(1000000)]
gen_exp = (x for x in range(1000000))

print(sys.getsizeof(list_comp)) # 约 8000056 字节
print(sys.getsizeof(gen_exp)) # 约 112 字节,内存占用极小

5.3 使用场景

生成器表达式适合只需遍历一次的场景,例如求和、取最大值:

total = sum(x * x for x in range(1000)) # 无需中间列表
print(total)

6. 实战案例

6.1 大文件读取

一次性读取大文件会占用大量内存,甚至导致内存溢出。使用生成器逐行读取,内存占用恒定:

def read_large_file(file_path):
with open(file_path, "r", encoding="utf-8") as f:
for line in f:
yield line.strip()

# 逐行处理,内存占用与文件大小无关
for line in read_large_file("huge_log.txt"):
if "ERROR" in line:
print(line)

6.2 无限序列

生成器可以轻松构造无限序列,配合 itertools.islice 按需取用:

import itertools

def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b

# 取前 10 个斐波那契数
fib = fibonacci()
print(list(itertools.islice(fib, 10)))
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

6.3 流式处理管道

生成器可以串联成处理管道,每个环节只处理一个元素,内存占用始终恒定:

def read_lines(file_path):
with open(file_path, "r", encoding="utf-8") as f:
for line in f:
yield line

def filter_keyword(lines, keyword):
for line in lines:
if keyword in line:
yield line

def count_words(lines):
for line in lines:
yield len(line.split())

# 管道:读取 → 过滤 → 统计,全程惰性
lines = read_lines("data.txt")
filtered = filter_keyword(lines, "Python")
word_counts = count_words(filtered)

for count in word_counts:
print(count)

7. 总结

理解迭代器与生成器,核心要点如下:

  • 可迭代对象实现 __iter__(),可被 for 遍历;迭代器额外实现 __next__(),遍历后耗尽。
  • iter() 把可迭代对象转为迭代器,next() 逐个取元素,耗尽时抛 StopIteration。
  • yield 生成器采用惰性计算,按需产出元素,内存占用恒定。
  • 生成器表达式比列表推导式更省内存,适合只需遍历一次的场景。
  • 大文件读取、无限序列、流式管道是生成器的典型应用。

掌握惰性计算的思维,能让你在处理海量数据时写出更高效、更省内存的 Python 代码。

赞(0)
未经允许不得转载:171主机测评 » Python 迭代器与生成器:惰性计算与内存优化
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址