当前位置:首页>python>Python反向索引与切片:一行代码实现字符串/列表反转

Python反向索引与切片:一行代码实现字符串/列表反转

  • 2026-10-11 07:37:26
Python反向索引与切片:一行代码实现字符串/列表反转

掌握Python的“时光倒流”魔法,让代码优雅10倍

一、一个让初学者目瞪口呆的操作

先看一段代码:

s = "Python编程"
print(s[::-1])

输出:

程编nohtyP

就一行?没有循环?没有反转函数?直接 [::-1] 就把字符串倒过来了!

这还不是最神奇的。再看:

lst = [1, 2, 3, 4, 5]
print(lst[::-1])   # [5, 4, 3, 2, 1]
print(lst[::2])    # [1, 3, 5]  ← 隔一个取一个
print(lst[1:4])    # [2, 3, 4]  ← 截取子列表

Python 的切片语法,就像一个万能遥控器,让你随心所欲地“切割”序列。

二、先从“反向索引”说起

在大多数编程语言里,获取最后一个元素需要先知道长度:

arr = [10, 20, 30, 40, 50]
last = arr[len(arr) - 1]   # 太麻烦了

Python 开挂了:负数索引从右向左数

arr = [10, 20, 30, 40, 50]
print(arr[-1])    # 50 —— 最后一个
print(arr[-2])    # 40 —— 倒数第二个
print(arr[-5])    # 10 —— 第一个

示意图:

索引:   -5     -4     -3     -2     -1
       ┌────┬────┬────┬────┬────┐
       │ 10 │ 20 │ 30 │ 40 │ 50 │
       └────┴────┴────┴────┴────┘
索引:    0     1     2     3     4

💡 初学者笔记:负索引从 -1 开始(最后一个),依次递减。

三、切片完整语法:[起始:结束:步长]

切片不是魔法,它有一套清晰规则:

sequence[起始:结束:步长]
参数
含义
可省略?
起始
从哪个索引开始(包含)
✅ 默认 0
结束
到哪个索引结束(不包含)
✅ 默认到末尾
步长
每隔几个取一个(可为负数)
✅ 默认 1

3.1 基础示例

s = "0123456789"

print(s[2:5])     # "234"  (索引2,3,4)
print(s[:5])      # "01234"(从开头到索引5)
print(s[5:])      # "56789"(从索引5到末尾)
print(s[:])       # "0123456789"(整个)
print(s[1:8:2])   # "1357" (步长为2)

3.2 负步长:实现反转的奥秘

当 步长为负数 时,切片会从右向左移动:

s = "12345"
print(s[::-1])   # "54321"

拆解执行过程:

  1. 步长 -1,表示倒着走
  2. 起始和结束都省略,表示从最右走到最左
  3. 结果就是完全反转

等价写法(但不推荐):

s[-1::-1]   # 从最后一个走到开头
# 或
s[::-1]     # 最简洁

3.3 负步长的更多玩法

s = "abcdef"

print(s[4:1:-1])   # "edc" —— 从索引4走到索引2(因为结束索引1不包含)
print(s[5::-2])    # "fdb" —— 从索引5开始,步长-2

四、实战应用:反转的N种用法

4.1 判断回文字符串(经典面试题)

defis_palindrome(s):
# 去掉空格并统一小写
    s = s.replace(" ", "").lower()
return s == s[::-1]

print(is_palindrome("上海自来水来自海上"))  # True
print(is_palindrome("A man a plan a canal panama"))  # True

一行代码版本:

is_pal = lambda s: (t := s.replace(" ", "").lower()) == t[::-1]

4.2 反转列表并保留原列表

original = [1, 2, 3, 4]
reversed_copy = original[::-1]   # 新列表 [4,3,2,1]
print(original)                   # 原列表不变 [1,2,3,4]

# 对比:.reverse() 会原地修改
original.reverse()
print(original)                   # [4,3,2,1] (原列表被改了)

4.3 批量提取对角线元素

matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# 主对角线
diag1 = [matrix[i][i] for i in range(3)]           # [1,5,9]
# 副对角线(反转)
diag2 = [matrix[i][2-i] for i in range(3)]         # [3,5,7]

# 更骚的写法:先反转行再取主对角线
diag2 = [row[::-1][i] for i, row in enumerate(matrix)]  # [3,5,7]

4.4 字符串轮转判断

defis_rotation(s1, s2):
"""判断s2是否是s1的旋转(如"abcde"旋转后是"cdeab")"""
return len(s1) == len(s2) and s2 in s1 + s1

print(is_rotation("abcde", "cdeab"))  # True
# 原理:旋转后的字符串一定是原字符串拼接后的子串

五、进阶技巧:切片赋值

切片不仅能取值,还能赋值(可迭代对象):

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

# 替换一段
nums[1:3] = [20, 30]   # [1,20,30,4,5]

# 删除一段
nums[2:4] = []         # [1,20,5]

# 步长赋值(数量必须匹配)
nums = [1,2,3,4,5]
nums[::2] = [10,30,50]  # [10,2,30,4,50]

# 反转并赋值(骚操作)
nums = [1,2,3,4,5]
nums[:] = nums[::-1]    # 原地反转
print(nums)             # [5,4,3,2,1]

六、常见陷阱与注意事项

❌ 陷阱1:字符串反转的误解

s = "hello"
s[::-1]      # 正确返回新字符串
s.reverse()  # ❌ 报错!字符串没有reverse方法

❌ 陷阱2:超大步长的性能问题

# 虽然可以,但不推荐在大数据上使用
big_list = list(range(10**6))
reversed_list = big_list[::-1]   # 复制整个列表,内存翻倍

# 如果需要迭代,用 reversed() 更省内存
for item in reversed(big_list):   # 惰性迭代,不复制
pass

❌ 陷阱3:对字典使用切片

d = {"a":1, "b":2}
d[::-1]   # ❌ TypeError: 'dict' object is not subscriptable

切片只适用于序列类型:字符串、列表、元组、range 等。

七、趣味挑战(答案在文末)

挑战1:用一行代码实现“每隔一个字符反转”

输入:"abcdefgh"
输出:"hgfedcba"(这太简单了)
进阶要求:输出 "h f d b"(索引7,5,3,1) 和 "g e c a"(索引6,4,2,0)

挑战2:不使用 [::-1],手动实现一个切片反转函数

defmy_reverse(seq):
# 不准用 [::-1] 或 reversed()
# 要求:支持字符串、列表、元组
pass

八、总结

用法
代码
说明
完全反转
s[::-1]
最常用,最优雅
隔一个取一个
s[::2]
偶数位置
倒着隔一个
s[::-2]
反转后隔一取
取后三个
s[-3:]
负索引+省略结束
去头去尾
s[1:-1]
删除首尾
复制列表
lst[:]
浅拷贝

一句话记住切片:

“冒号分隔,起始结束,步长可负,不写全包。”

挑战答案

挑战1:

s = "abcdefgh"
result1 = s[7::-2]   # "hfdb"
result2 = s[6::-2]   # "geca"
print(result1, result2)

挑战2(参考实现):

defmy_reverse(seq):
# 元组/列表/字符串通用
if isinstance(seq, (str, list, tuple)):
# 用负步长的range构造索引
        indices = range(len(seq)-1, -1, -1)
        result = [seq[i] for i in indices]
# 还原类型
if isinstance(seq, str):
return''.join(result)
elif isinstance(seq, tuple):
return tuple(result)
return result
raise TypeError("只支持序列类型")

思考题(欢迎评论区留言):

matrix = [[1,2,3],[4,5,6],[7,8,9]]
# 如何用切片一步得到 [[9,8,7],[6,5,4],[3,2,1]] ?

最新文章

随机文章