将字符串 "ilovechina" 进行反转。
昨天晚上十一点多,在公司楼下等外卖,风有点大,我正抱着杯温豆浆取暖。我们组那个小李在群里问:哥,Python里把字符串反过来,最靠谱的是哪种写法?我当时回了他一句“看你图啥”,然后就有这篇……咳,随手记一下,免得大家半夜翻聊天记录。
最短那一招
别拐弯,Python 自带“倒着切”的语法,读起来像“从头到尾,步长是 -1”。
s = "ilovechina"
print(s[::-1]) # anihcevoli
两个点说清楚: 1)字符串是不可变的,这一刀会新建一个字符串; 2)时间复杂度 O(n)、空间也差不多 O(n),n 是长度。
迭代器那一招
有的人对切片步长不眼熟,想语义更直给一点,就用 reversed 配合 str.join。可读性不错,面试被追问时也好解释。
s = "ilovechina"
print(''.join(reversed(s))) # anihcevoli
reversed(s) 只给你一个从后往前的迭代器,join 负责把它们粘回成字符串。
自己换位
有时你就是想“手撸”一下(比如你在讲解“原地反转”的思路)。字符串不能原地改,就先变成列表,双指针对撞、交换,最后再拼回去。
defreverse_two_pointer(s: str) -> str:
chars = list(s)
i, j = 0, len(chars) - 1
while i < j:
chars[i], chars[j] = chars[j], chars[i]
i += 1
j -= 1
return''.join(chars)
print(reverse_two_pointer("ilovechina")) # anihcevoli
思路清晰,写起来也不长,就是比前两招啰嗦一点。
递归?可以,但没必要
递归能写,教学味儿重,而且会走栈,字符串长了性能就一般般,还可能遇到递归深度限制(默认一千多层)。
defreverse_recursive(s: str) -> str:
if len(s) <= 1:
return s
mid = len(s) // 2
return reverse_recursive(s[mid:]) + reverse_recursive(s[:mid])
print(reverse_recursive("ilovechina")) # anihcevoli
除非你在刷题练味儿,不然真不推荐放线上。
Unicode 和“看起来一个字符”
这次我们反的是纯英文字母,没坑。但顺便提一句:有些看起来是“一个字符”的东西,其实是多个 code point 组合(比如某些旗帜 emoji、带音调的字母)。简单的反转会把组合关系拆散,显示就怪了。要处理这种“字形簇”,得上更懂文本边界的库(regex 的 \X 分组,或第三方分词/规范化工具)。不过 "ilovechina" 没这事,放心。
随手用 assert 做个自检,团队里跑脚本时也安心一点。
defreverse_pythonic(s: str) -> str:
return s[::-1]
defreverse_iter(s: str) -> str:
return''.join(reversed(s))
defreverse_two_pointer(s: str) -> str:
chars = list(s)
i, j = 0, len(chars) - 1
while i < j:
chars[i], chars[j] = chars[j], chars[i]
i += 1
j -= 1
return''.join(chars)
target = "ilovechina"
expect = "anihcevoli"
for fn in (reverse_pythonic, reverse_iter, reverse_two_pointer):
out = fn(target)
print(fn.__name__, "=>", out)
assert out == expect
如果你手边有 pytest,把这段塞进测试也没问题。
哪个更合适?
想短平快: s[::-1]。追求语义直白: ''.join(reversed(s))。需要讲“原理”或在受限环境:双指针换位版。
性能上三者都差不离,切片通常会快一点;但说真的,这么短的字符串,可读性比纳秒更重要。昨晚我回小李一句“用切片吧,睡觉”,他回了个“6”。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领