导读:本期聚焦于韩兆瑞创作的《Python中lst[::-1]和reversed(lst)的内存占用到底差多少?》,敬请观看详情。lst[::-1] 本质上是列表切片语法,它会遍历原列表并创建一个包含所有元素的新列表,因此内存占用与原列表规模成正比。而 reversed(lst) 调用返回的是一个 list_reverseiterator 迭代器对象,它只保存对原列表的引用,并不会复制元素。在处理包含数百万个整数的列表时,前者的额外内存可能达到数十兆字节,后者几乎只有几十个字节。本文通过 sys.getsizeof 和 tracemalloc 模块测量两种写法的真实内存变化,展示不同场景下的数据,并分析为什么 reversed 对象不能像列表那样随机访问。理解这些差异有助于在内存敏感的循环、函数参数传递等场景中做出更合理的选择。另外,切片操作还会带来复制元素的时间开销,而 reversed 创建过程接近于常量时间。

在Python中,想要反向遍历列表,常见的写法有两种:一种是使用切片语法 lst[::-1],另一种是调用内置函数 reversed(lst)。两者在代码可读性上差别不大,但底层行为完全不同。前者会立刻生成一个全新的列表对象,后者只返回一个轻量级的迭代器。正是这种差异,导致它们的内存占用可能相差几个数量级。

Python中lst[::-1]和reversed(lst)的内存占用到底差多少?

切片创建新列表:lst[::-1] 的内存模型

列表切片是Python中最常用的语法之一,它的实现方式并不是返回原列表的视图,而是分配一块新的内存区域,把选中的元素指针逐个复制过去。对于 lst[::-1],步长为 -1,解释器从列表尾部开始读取元素,再按顺序写入新列表。这意味着新列表的长度和原列表相同,每个槽位都需要占用一个指针大小的空间。在64位CPython中,这个指针大小是8字节,因此仅指针数组部分就需要大约 8 * N 字节,再加上列表对象本身的固定开销(通常为56字节左右)。

除了指针数组,新列表还持有对原列表中元素的引用。由于整数、字符串等不可变对象并不会被复制,只是引用计数增加,所以元素本身的内存不会被复制。但对于大型列表来说,仅仅复制指针数组已经是一笔不小的开销。例如一个包含一千万个整数的列表,lst[::-1] 操作会额外分配约 80MB 的内存用于存放指针,这在内存受限的环境下可能导致性能下降甚至内存溢出。

切片操作还有一个隐藏成本:复制过程需要遍历原列表。虽然步长为 -1 的遍历速度很快,但在元素数量非常大时,依然会产生可感知的时间延迟。与之相比,reversed(lst) 的创建过程几乎不需要遍历,只是返回一个携带原列表引用的迭代器对象。

import sys

lst = list(range(1000000))
sliced = lst[::-1]
rev = reversed(lst)

print("原列表大小:", sys.getsizeof(lst))
print("切片新列表大小:", sys.getsizeof(sliced))
print("reversed迭代器大小:", sys.getsizeof(rev))

上面的代码中,原列表和切片新列表的大小都接近 8MB,而 reversed 迭代器的大小通常只有几十字节。sys.getsizeof 返回的是对象本身占用的内存,列表对象的大小直接反映了指针数组的规模,因此能清晰看出差异。

用 tracemalloc 追踪内存分配:真实对比数据

sys.getsizeof 只能返回对象本身占用的内存,无法反映对象引用的其他内存。对于列表来说,它返回的是列表对象加上指针数组的大小,这部分已经足够看出差异。但 tracemalloc 可以追踪 Python 进程的堆内存分配,能够更精确地观察某段代码执行前后的内存峰谷变化。

下面这段代码使用 tracemalloc 分别测量执行 lst[::-1] 和 reversed(lst) 时的内存峰值增量。需要说明的是,tracemalloc 会追踪所有 Python 对象的内存分配,包括临时对象,因此结果会比 sys.getsizeof 略高,但趋势完全相同。

import tracemalloc

lst = list(range(10000000))
tracemalloc.start()

current1, peak1 = tracemalloc.get_traced_memory()
_ = lst[::-1]
current2, peak2 = tracemalloc.get_traced_memory()
print("切片操作内存增量:", current2 - current1)
print("切片操作峰值增量:", peak2 - peak1)

current3, peak3 = tracemalloc.get_traced_memory()
_ = reversed(lst)
current4, peak4 = tracemalloc.get_traced_memory()
print("reversed操作内存增量:", current4 - current3)
print("reversed操作峰值增量:", peak4 - peak3)

tracemalloc.stop()

在上面的示例中,切片操作的内存增量通常接近 80MB(一千万个指针乘以8字节),而 reversed 操作的内存增量只有几十到几百字节,因为它只创建了一个迭代器对象。即使把 tracemalloc 自身的开销考虑进去,两者的差距依然非常悬殊。

需要注意的是,tracemalloc 只能追踪 Python 对象,不能追踪底层 C 语言库自己分配的内存。不过对于列表切片来说,所有内存都由 Python 内存管理器分配,所以结果是可信的。这个对比清晰地说明了为什么在循环或函数中反复使用 lst[::-1] 可能会导致内存快速膨胀。

reversed(lst) 的限制:迭代器不能随机访问

reversed(lst) 返回的对象类型是 list_reverseiterator,它实现了迭代器协议,只能通过 next() 或者 for 循环逐个获取元素。与列表最大的区别在于,它不支持索引、切片、len() 等操作。如果需要获取第 k 个元素,或者对反向结果再做一次切片,就必须先用 list(reversed(lst)) 把它转换成一个新列表,这会重新引入内存开销。

下面这个简单的例子展示了 reversed 迭代器的典型错误用法。如果尝试直接通过下标访问,会抛出 TypeError。理解这一点有助于避免写出看似合理但实际无效的代码。

lst = [10, 20, 30, 40, 50]
rev = reversed(lst)

print(next(rev))  # 50
print(next(rev))  # 40

# 下面这行会抛出 TypeError: 'list_reverseiterator' object is not subscriptable
# print(rev[0])

# 如果需要索引,只能转换回列表
rev_list = list(reversed(lst))
print(rev_list[1])  # 40

对于只需要一次性反向遍历的场景,比如对列表中的元素执行累加、打印或者调用某个函数,reversed(lst) 是理想选择。它在不复制数据的前提下提供了高效的反向访问能力。但如果反向结果需要被多次使用、随机访问或者作为参数传递给需要列表对象的函数,那么使用 lst[::-1] 或 list(reversed(lst)) 会更方便,同时也必须接受额外的内存代价。

另一个值得注意的点是,reversed 迭代器与原列表是紧密关联的。如果在迭代过程中修改原列表,比如删除元素或插入元素,迭代器的行为可能会变得难以预测。这一点与大多数容器迭代器类似,使用时需要谨慎。

如何根据场景选择:内存、速度与可读性的平衡

从内存角度看,reversed(lst) 几乎总是优于 lst[::-1],尤其是在列表规模很大的时候。但内存并不是唯一的决策因素。切片操作返回的是真正的列表,可以随机访问、多次遍历,而且代码意图非常直观。当列表较小、或者反向列表需要频繁随机访问时,lst[::-1] 的额外内存开销通常可以忽略不计,反而能带来更好的可读性和灵活性。

从执行速度来看,创建 lst[::-1] 需要复制指针数组,时间复杂度是 O(N),而创建 reversed(lst) 是 O(1)。但在创建之后的遍历阶段,两者的遍历速度非常接近,因为它们都是顺序访问原列表的指针。真正的差别在于是否愿意支付一次性复制成本。如果反向结果只遍历一次,reversed 的总时间通常更短;如果反向结果需要被多次遍历,切片版本可能因少了每次从原列表反向查找的开销而略占优势。

总结起来,一个简单的决策原则是:如果只需要一次性反向遍历,优先使用 reversed(lst);如果需要把反向结果保存下来并重复使用,使用 lst[::-1] 或 list(reversed(lst)),但要意识到内存成本。对于超大列表且内存敏感的场景,甚至可以考虑使用 range(len(lst)-1, -1, -1) 配合索引来避免创建任何新列表,但代码可读性会下降。

最后提醒一下,Python 中的字符串同样支持切片和 reversed,但字符串切片也会创建新字符串,内存行为与列表类似。然而字符串的不可变性导致没有可迭代的轻量级反向视图,reversed(str) 依然返回迭代器,但不会复制字符。这个规律在其他序列类型如元组、字节串上也基本成立。

Python列表切片reversed内存使用量修改时间:2026-08-16 21:06:23

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。