Skip to content

切片

切片用来一次取出连续的一段,写法是 对象[起点:终点:步长]。

字符串 和 列表 里已经练过最常见的 s[起点:终点]。这一节把三种对象、省略、负数和步长放在一起。规则和 range 一样:包含起点,不包含终点。

为什么需要切片

一个个用下标取,只能拿到一个元素:

s = "abcdefghijkl"
print(s[1])
print(s[5])
b
f

要的是从 b 到 e 这一段时,不必写循环。方括号里用冒号隔开起点和终点:

print(s[1:5])
bcde

下标 1 是 b,算进去。下标 5 是 f,不算。所以是 bcde,四个字符。个数用终点减起点:5 - 1 = 4。

步长

第三个数是步长,表示隔几个取一个。不写时步长是 1。

print(s[0:5:1])
print(s[0:5:2])
print(s[0:5:3])
abcde
ace
ad

s[0:5:2] 从下标 0 开始,每次加 2,不超过 5:0、2、4,也就是 a、c、e。s[0:5:3] 取 0 和 3,结果是 ad。

整段都隔一个取一个时,起点和终点可以都不写:

print(s[::2])
print(s[1::2])
acegik
bdfhjl

s[::2] 是偶数下标,s[1::2] 是奇数下标。

省略写法

冒号要留着,缺的那一段用默认值。

写法 含义
s[:5] 从开头到下标 5,不包含 5
s[5:] 从下标 5 一直到末尾
s[:] 从头到尾,得到一份完整副本
s[::2] 从头到尾,步长为 2
print(s[:5])
print(s[5:])
print(s[:])
abcde
fghijkl
abcdefghijkl

s[:] 看起来和 s 一样。对字符串来说内容相同;对列表来说,它是一个新列表,后面改副本不会动到原来的那份。

负数下标

负数从末尾数,-1 是最后一个,和单个下标的规则一样。放进切片里,仍然包含起点、不包含终点。

print(s[-3:])
print(s[:-3])
print(s[-5:-1])
jkl
abcdefghi
hijk
  • s[-3:] 是最后三个字符
  • s[:-3] 去掉最后三个
  • s[-5:-1] 从倒数第 5 个取到倒数第 2 个,最后一个 l 不算

反序

步长写成 -1,就从后往前取。方向必须和步长一致:往回走时,起点要在终点的右边。

print(s[5:0:-1])
print(s[::-1])
fedcb
lkjihgfedcba

s[5:0:-1] 从下标 5 的 f 取到下标 1 的 b,下标 0 的 a 不算。s[::-1] 不写起点和终点,整串倒过来,这个写法很常用。

起点比终点大、步长却是正数时,一个字符都取不到,结果是空字符串,也不报错:

print(repr(s[5:0]))
''

要倒序,必须把步长写成负数:s[5:0:-1]。

下标越界和切片越界

取单个下标时,超出长度会报错。切片的终点写得比长度大,只取到末尾。

s1 = "hello"
print(f"s1的长度{len(s1)}")
print(s1[4])
print(s1[0:4])
print(s1[0:10])
s1的长度5
o
hell
hello

len(s1) 是 5,最后一个下标是 4。s1[0:4] 不包含最后的 o。s1[0:10] 终点超出了,结果仍是整个 "hello"。

起点太小、终点太大,都会被收回到有效范围内。完全对不上时,得到空串:

print(s1[-100:2])
print(repr(s1[100:200]))
he
''

s1[100:200] 是空字符串。单个下标 s1[10] 则会报 IndexError。

列表和元组

列表、元组的切片写法和字符串完全一样。差别是结果的类型跟着原来的对象走:字符串切片还是字符串,列表切片还是列表,元组切片还是元组。

nums = [0, 1, 2, 3, 4]
print(nums[1:4])
print(nums[::2])

t = (0, 1, 2, 3, 4)
print(t[1:4])
[1, 2, 3]
[0, 2, 4]
(1, 2, 3)

切片得到的是新对象。改列表切片里的元素,原来的列表不变:

a = [1, 2, 3, 4]
b = a[1:3]
b[0] = 100

print(b)
print(a)
[100, 3]
[1, 2, 3, 4]

这和 b = a 不同。b = a 只是又起了一个名字,两边是同一份列表。真要独立的一段,用切片。

字符串和元组不能给切片赋值,列表可以。赋值时,右边的一段会替换左边标出的那一段,两边长度不必相同:

nums = [0, 1, 2, 3, 4]
nums[1:3] = [8, 9]
print(nums)
[0, 8, 9, 3, 4]

右边写空列表,就是删掉这一段:

nums = [0, 1, 2, 3, 4]
nums[1:4] = []
print(nums)
[0, 4]

字符串没有这个操作:

text = "hello"
text[0:1] = "H"
TypeError: 'str' object does not support item assignment

要得到新字符串,把切片拼起来:"H" + text[1:]。

同一个切法用多次

slice 也是内置函数,不是新语法。它把起点、终点、步长收成一个对象,之后可以套在不同的字符串或列表上。

part = slice(1, 5)
nums = [0, 8, 9, 3, 4]
print(s[part])
print(nums[part])
print(part.start, part.stop, part.step)
bcde
[8, 9, 3, 4]
1 5 None

不写步长时,part.step 是 None,用起来和省略步长一样。平时直接写 [1:5] 更短。同一切法要重复用在好几份数据上时,先做成 slice 更清楚。

常见坑

少取一个。 s[1:5] 到下标 4 为止。想包含下标 5,终点要写成 6,或者直接写到末尾 s[1:]。

倒序忘了负步长。 s[5:0] 是空的。倒序写成 s[5:0:-1],整串倒过来写成 s[::-1]。

切片越界不报错,单个下标会报错。 s[0:100] 只取到末尾。s[100] 会 IndexError。

列表切片只复制了这一层。 元素本身如果又是列表,改内层时两边都会变:

a = [[1], [2], [3]]
b = a[:2]
b[0][0] = 9
print(a)
[[9], [2], [3]]

b[0] 和 a[0] 是同一个小列表。只是替换外层元素,例如 b[0] = [100],原来的 a 不会跟着变。

小结

  • 写法是 对象[起点:终点:步长],包含起点,不包含终点
  • 省略起点表示从头,省略终点表示到尾,[:] 是完整副本
  • 负数从末尾数,步长为负就倒着取
  • 切片越界得到空结果或截断,不会像单个下标那样报错
  • 字符串、列表、元组写法相同,结果类型和原来一样
  • 只有列表可以给切片赋值;字符串要改内容,用拼接得到新字符串

下一节学习 函数。用 python examples/slice.py 运行本节的例子。