首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Python的逆序把我坑惨了,原来reverse和reversed的区别这么大

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

原创
作者头像
风一样的男子
发布于 2026-10-14 10:34:52
发布于 2026-10-14 10:34:52
400
举报
文章被收录于专栏:编程教程编程教程

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

代码语言:javascript
复制
messages = get_messages()
messages = messages.reverse()

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

代码语言:javascript
复制
TypeError: 'NoneType' object is not iterable

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

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

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

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

先看一个最直观的对比

代码语言:javascript
复制
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))

输出:

代码语言:javascript
复制
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)。

代码语言:javascript
复制
lst = [1, 2, 3]
lst.reverse()
print(lst)  # [3, 2, 1]

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

代码语言:javascript
复制
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等),返回一个反向迭代器。

代码语言:javascript
复制
lst = [1, 2, 3]
it = reversed(lst)
print(it)          # <list_reverseiterator object at 0x...>
print(list(it))    # [3, 2, 1]

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

代码语言:javascript
复制
it = reversed([1, 2, 3])
print(list(it))  # [3, 2, 1]
print(list(it))  # [],已经耗尽了

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

代码语言:javascript
复制
reversed_list = list(reversed([1, 2, 3]))
print(reversed_list)  # [3, 2, 1]
print(reversed_list)  # [3, 2, 1],可以重复使用

reversed()不会修改原对象:

代码语言:javascript
复制
lst = [1, 2, 3]
list(reversed(lst))
print(lst)  # [1, 2, 3],原列表没变

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

代码语言:javascript
复制
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()返回的是字符的反向迭代器。如果你想要反转后的字符串,需要再拼回去:

代码语言:javascript
复制
s = "abc"
reversed_s = "".join(reversed(s))
print(reversed_s)  # "cba"

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

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

代码语言:javascript
复制
nums = [1, 2, 3]
nums = nums.reverse()  # nums 变成 None

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

代码语言:javascript
复制
nums = [1, 2, 3]
nums.reverse()
print(nums)  # [3, 2, 1]

或者用reversed():

代码语言:javascript
复制
nums = [1, 2, 3]
nums = list(reversed(nums))
print(nums)  # [3, 2, 1]

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

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

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

代码语言:javascript
复制
nums = [1, 2, 3]
reversed(nums)
print(nums)  # [1, 2, 3],没变

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

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

代码语言:javascript
复制
nums = [1, 2, 3]
nums = list(reversed(nums))
print(nums)  # [3, 2, 1]

或者用reverse()原地反转:

代码语言:javascript
复制
nums = [1, 2, 3]
nums.reverse()
print(nums)  # [3, 2, 1]

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

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

代码语言:javascript
复制
tup = (1, 2, 3)
tup.reverse()  # AttributeError

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

字符串也一样:

代码语言:javascript
复制
s = "abc"
s.reverse()  # AttributeError

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

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

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

代码语言:javascript
复制
it = reversed([1, 2, 3])
print(it[0])  # TypeError: 'list_reverseiterator' object is not subscriptable

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

代码语言:javascript
复制
lst = list(reversed([1, 2, 3]))
print(lst[0])  # 3

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

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

代码语言:javascript
复制
it = reversed([1, 2, 3])

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

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

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

代码语言:javascript
复制
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()本身是惰性的,如果你只是遍历一次,不需要创建完整列表,可能更省内存。

代码语言:javascript
复制
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)空间。

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

回到小孙的故事

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

代码语言:javascript
复制
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就不再是坑,而是两个各司其职的工具。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 先看一个最直观的对比
  • reverse():列表的原地反转方法
  • reversed():内置的反向迭代器函数
  • 坑一:把 reverse() 的返回值赋给变量
  • 坑二:以为 reversed() 会修改原列表
  • 坑三:对元组或字符串用 reverse()
  • 坑四:reversed() 返回迭代器,不能索引
  • 坑五:在循环中多次使用同一个 reversed 迭代器
  • 性能差异
  • 什么时候用 reverse(),什么时候用 reversed()?
  • 一个常见的误解:reversed() 比 reverse() 慢很多
  • 回到小孙的故事
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档