Python的逆序把我坑惨了,原来reverse和reversed的区别这么大

简介: Python中`reverse()`原地反转列表但返回`None`,误赋值将导致变量变`None`;`reversed()`则返回反向迭代器,不修改原对象。二者语义与适用场景截然不同,需按需选用,避免常见陷阱。(239字)

小孙那天写了一个消息队列的处理逻辑,需要把最近收到的消息按时间倒序排列。他拿到一个列表,里面是按时间正序排列的消息对象。他想都没想,写下了这行代码:

messages = get_messages()
messages = messages.reverse()

然后他拿着messages去遍历,准备处理最新的消息。结果程序直接报错:

TypeError: 'NoneType' object is not iterable

他盯着屏幕愣了五秒:messages明明是个列表,怎么变成None了?他明明记得reverse()就是用来反转列表的,为什么反转之后列表没了?

问题出在那个赋值上。reverse()是原地反转,它不返回反转后的列表,它返回None。小孙把None赋给了messages,原列表确实反转了,但他把变量指向了None,反转后的列表就这样被他亲手弄丢了。

“我就想倒个序,怎么还把数据搞没了?”

这个坑的根源,是他没搞清楚reverse和reversed的区别。这两个东西名字很像,功能也像,但行为完全不同。

代理 IP 使用小技巧 让你的数据抓取效率翻倍.png

先看一个最直观的对比

nums = [1, 2, 3, 4, 5]

# 方式一:reverse()
result1 = nums.reverse()
print("reverse() 之后 nums:", nums)
print("reverse() 的返回值:", result1)

# 方式二:reversed()
nums2 = [1, 2, 3, 4, 5]
result2 = reversed(nums2)
print("reversed() 之后 nums2:", nums2)
print("reversed() 的返回值:", result2)
print("reversed() 转成列表:", list(result2))

输出:

reverse() 之后 nums: [5, 4, 3, 2, 1]
reverse() 的返回值: None

reversed() 之后 nums2: [1, 2, 3, 4, 5]
reversed() 的返回值: <list_reverseiterator object at 0x...>
reversed() 转成列表: [5, 4, 3, 2, 1]

区别非常明显:

reverse()是列表的方法,它把原列表原地反转,原列表被修改,返回值是None。

reversed()是内置函数,它不碰原列表,返回一个迭代器,你需要遍历它或者转成列表才能看到反转后的结果。

用一个生活场景来理解。你有一排书,按顺序摆在书架上。

reverse()的做法是:直接把书架上的书顺序倒过来。第一本和最后一本交换,第二本和倒数第二本交换。书架上的书变了,但你手里没有新东西。

reversed()的做法是:不碰书架,而是给你一个“反向阅读指南”。你拿着这个指南,从最后一本开始读,读到第一本。书架本身纹丝不动,你只是换了一种方式去读它。

区别一目了然:一个改原数据,一个不改,返回的是一个“反向查看器”。

reverse():列表的原地反转方法

reverse()是列表对象的方法,没有参数,没有返回值(返回None)。

lst = [1, 2, 3]
lst.reverse()
print(lst)  # [3, 2, 1]

它只能用于列表。元组、字符串、字典都没有这个方法。

tup = (1, 2, 3)
tup.reverse()  # AttributeError: 'tuple' object has no attribute 'reverse'

s = "abc"
s.reverse()    # AttributeError: 'str' object has no attribute 'reverse'

因为元组和字符串是不可变的,原地修改根本不可能。所以它们没有reverse方法。

reverse()的时间复杂度是O(n),因为它需要交换列表两端的元素,遍历一半的长度。空间复杂度是O(1),不需要额外空间。

reversed():内置的反向迭代器函数

reversed()是Python的内置函数,接受任何可迭代对象(序列、列表、元组、字符串、range等),返回一个反向迭代器。

lst = [1, 2, 3]
it = reversed(lst)
print(it)          # <list_reverseiterator object at 0x...>
print(list(it))    # [3, 2, 1]

注意:reversed()返回的不是列表,是一个迭代器。迭代器只能遍历一次。

it = reversed([1, 2, 3])
print(list(it))  # [3, 2, 1]
print(list(it))  # [],已经耗尽了

如果你需要多次使用反转结果,必须转成列表:

reversed_list = list(reversed([1, 2, 3]))
print(reversed_list)  # [3, 2, 1]
print(reversed_list)  # [3, 2, 1],可以重复使用

reversed()不会修改原对象:

lst = [1, 2, 3]
list(reversed(lst))
print(lst)  # [1, 2, 3],原列表没变

它适用于任何可迭代对象,包括元组和字符串:

print(list(reversed((1, 2, 3))))    # [3, 2, 1]
print(list(reversed("abc")))        # ['c', 'b', 'a']
print(list(reversed(range(5))))     # [4, 3, 2, 1, 0]

对于字符串,reversed()返回的是字符的反向迭代器。如果你想要反转后的字符串,需要再拼回去:

s = "abc"
reversed_s = "".join(reversed(s))
print(reversed_s)  # "cba"

坑一:把 reverse() 的返回值赋给变量

这是最常见的错误,小孙踩的就是这个。

nums = [1, 2, 3]
nums = nums.reverse()  # nums 变成 None

正确的写法是直接调用,不接收返回值:

nums = [1, 2, 3]
nums.reverse()
print(nums)  # [3, 2, 1]

或者用reversed():

nums = [1, 2, 3]
nums = list(reversed(nums))
print(nums)  # [3, 2, 1]

这个错误和之前讨论过的sort()是同一个问题:原地修改的方法返回None,这是Python的设计规则。所有原地修改的方法都不返回修改后的对象,而是返回None。

坑二:以为 reversed() 会修改原列表

反过来,有些人用了reversed(),以为原列表被反转了:

nums = [1, 2, 3]
reversed(nums)
print(nums)  # [1, 2, 3],没变

reversed()返回的迭代器没有被使用,原列表纹丝不动。

正确的写法是接收迭代器并遍历,或者转成列表:

nums = [1, 2, 3]
nums = list(reversed(nums))
print(nums)  # [3, 2, 1]

或者用reverse()原地反转:

nums = [1, 2, 3]
nums.reverse()
print(nums)  # [3, 2, 1]

坑三:对元组或字符串用 reverse()

reverse()只能用于列表。如果你有一个元组想反转,只能用reversed():

tup = (1, 2, 3)
tup.reverse()  # AttributeError

result = tuple(reversed(tup))
print(result)  # (3, 2, 1)

字符串也一样:

s = "abc"
s.reverse()  # AttributeError

result = "".join(reversed(s))
print(result)  # "cba"

坑四:reversed() 返回迭代器,不能索引

reversed()返回的是迭代器,不是列表。迭代器不支持索引和切片:

it = reversed([1, 2, 3])
print(it[0])  # TypeError: 'list_reverseiterator' object is not subscriptable

如果你需要索引访问,必须先转成列表:

lst = list(reversed([1, 2, 3]))
print(lst[0])  # 3

坑五:在循环中多次使用同一个 reversed 迭代器

迭代器只能遍历一次。如果你在循环里用了reversed(),然后想再遍历一次,会发现第二次是空的。

it = reversed([1, 2, 3])

for x in it:
   print(x)  # 3, 2, 1

for x in it:
   print(x)  # 什么也没有

如果需要多次遍历,要么每次都调用reversed(),要么转成列表:

lst = list(reversed([1, 2, 3]))
for x in lst:
   print(x)
for x in lst:
   print(x)

性能差异

reverse()是原地反转,不需要额外空间,时间复杂度O(n)。

reversed()返回迭代器,不复制数据,也是O(1)空间(迭代器本身很小)。但如果你转成列表list(reversed(...)),就需要O(n)额外空间。

在时间上,reverse()和list(reversed(...))差不多,都是O(n)。但reversed()本身是惰性的,如果你只是遍历一次,不需要创建完整列表,可能更省内存。

import timeit

lst = list(range(10000))

t1 = timeit.timeit(lambda: lst.copy().reverse(), number=10000)
t2 = timeit.timeit(lambda: list(reversed(lst)), number=10000)

print(f"reverse(): {t1:.4f}")
print(f"list(reversed()): {t2:.4f}")

在我的机器上,两者都在0.5秒左右,差距很小。

什么时候用 reverse(),什么时候用 reversed()?

判断标准很简单:

需要原地反转列表,不保留原顺序:用reverse()。

需要保留原列表,或者处理非列表对象:用reversed()。

只需要遍历一次反向序列:用reversed(),不需要转列表。

需要多次使用反向结果,或者需要索引访问:用list(reversed(...))。

对元组、字符串、range等不可变对象反转:只能用reversed()。

一个常见的误解:reversed() 比 reverse() 慢很多

有些人觉得reversed()返回迭代器,每次遍历都要计算下一个元素,所以比reverse()慢。其实不然。

reverse()需要交换元素,遍历一半长度。reversed()的迭代器在每次next()时计算下一个位置,总共也是O(n)。两者时间复杂度相同。

真正的区别在于内存:reverse()原地修改,不占额外空间;reversed()如果不转列表,也不占额外空间。如果你转成列表,就需要O(n)空间。

所以选择哪个,主要看语义需求,而不是性能。

回到小孙的故事

小孙最终的代码是这样的:

messages = get_messages()
messages.reverse()  # 原地反转
# 或者
messages = list(reversed(messages))  # 生成新列表

他选择了reverse(),因为他不需要保留原顺序,而且原列表只有他一个人在用。反转之后,他遍历messages,处理最新的消息,一切正常。

他后来在团队文档里写了一条:“reverse()原地反转列表,返回None。reversed()返回反向迭代器,不修改原对象。需要保留原顺序就用reversed(),不需要就用reverse()。别把reverse()的返回值赋给变量。”

这个区分看起来简单,但真到写代码的时候,很多人会随手写nums = nums.reverse(),然后在下一行打印时发现是None。或者用reversed()之后想用列表方法,发现迭代器没有那些方法。

记住那个书架的比喻:reverse()是直接把书架上的书倒过来,reversed()是给你一个反向阅读指南,书架本身不动。你要保留书架原样,就用reversed();你不在乎书架顺序,只想把它倒过来,就用reverse()。

逆序本身不难,难的是想清楚“这个逆序会不会影响别的地方”。想清楚了,reverse和reversed就不再是坑,而是两个各司其职的工具。

目录
相关文章
|
23天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
9365 30
|
9天前
|
存储 人工智能 并行计算
大模型本地部署终端选型方法论:以 Qwen3.8-27B 为例的四档分层完整流程
本文提出一套大模型本地部署终端选型方法论:定约束、定档位、定框架、定参数四步决策法,配合入门、主力、质量、无损四档分层模型。以 Qwen3.8-27B 实测数据为例,逐环节解读显存、带宽、存储、散热、系统、预算等要素,给出面向不同预算的优选方案、决策自查清单与市场观察框架。文末前瞻 AI 笔记本的 CPU+GPU 与统一内存两条路线,论证四步决策法在新品类上的延续性。
|
8天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型深度拆解:原理解读、实战测评与保姆级落地教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为**System One Model(系统一模型)**,对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
638 1
|
21天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2478 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
4天前
|
人工智能 JSON 自然语言处理
2026 年 Jev 决策模型全解析:核心原理拆解,性能实战测评与落地部署教程
有一款特殊AI模型在开发者圈子刷屏,它摒弃传统大模型擅长的对话聊天能力,专注做高速结构化决策,它就是TypeSafe AI推出的Jev模型。该模型由ChatGPT共同发明人Diogo Almeida主导研发,定位为System One Model(系统一模型),对标人类大脑快速直觉判断的思维模式,在响应延迟、调用成本、结构化输出稳定性上相比传统生成式大模型有着巨大差异。本文会完整拆解Jev底层原理、三大核心原语能力、适用业务场景,同时提供可直接运行的curl、Python代码示例,并且结合多组实测数据,客观分析模型优势与能力边界,帮助普通开发者和AI应用从业者快速上手落地。
413 0
|
3天前
|
存储 缓存 测试技术
QWT 3D 模块重构记:旧渲染管线改进介绍及实时瀑布图示例
QWT 3D模块完成重大重构:解耦窗口与图形(Plot+Item),摒弃OpenGL 1.x固定管线,迁至Core Profile着色器渲染;支持多图元同场景叠加,并以实时3D瀑布图验证——滑动窗口、固定色域、64帧×256点达61fps。兼容Qt 5.12+/6,LGPL协议。
157 120
|
15天前
|
Windows
【保姆级】.NET Framework 3.5离线安装教程(2026最新实测)
.NET Framework 3.5是微软经典运行库,专为兼容老软件、老游戏设计。Win7自带,Win10/11需手动启用(在线或离线安装)。它与4.x系列并存不冲突,缺它会导致“找不到组件”报错。本文详解两种安装方式及常见问题解决,小白也能轻松上手。(239字)
1874 0
|
10天前
|
人工智能 Linux Windows
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面
千问办公(QwenWork)是阿里云推出的AI智能办公平台,支持网页端直接使用及Windows/Mac/Linux客户端下载。提供PPT生成、财报分析、网页搭建等AI功能,个人版免费,企业版198元/席/月。详情见官网qwenwork.cn或阿里云产品页。
1194 0
千问办公(QwenWork)官网入口:其实有2个,一个是网页端千问办公,一个是介绍指南页面