# Python切片

## 定义

**切片**用`sequence[start:stop:step]` 从序列中取出一个子序列，区间左闭右开，取的是下标满足 $start \le i < stop$ 的元素。

* `start`：起始下标，包含在结果里。
* `stop`：结束下标，不包含在结果里。
* `step`：步长，默认`1`；为负时从右向左取。

索引从`0`开始。长度为 $n$ 的序列，正索引是`0`到`n-1`，负索引是 `-1`（最后一个）到 `-n`（第一个），`-k`等价于`n-k`。省略的参数由`step`的符号决定默认值：

| 参数      | `step > 0` | `step < 0`      |
| :------ | :--------- | :-------------- |
| `start` | `0`        | `len-1`，即最后一个元素 |
| `stop`  | `len`，越过末尾 | `-len-1`，越过开头   |

## 切片的行为

### 步长决定方向和间隔

* `step`为正：从`start`向右取，停在`stop`之前，每隔`step`个位置取一个；
* `step`为负：从`start`向左取，停在`stop`之后；
* `step`不能为`0`，否则抛出`ValueError`。

### 越界只影响结果，不报错

切片会把越界的端点截到序列边界内，因此结果可能是原序列的一部分，也可能是空序列，但不会抛异常。直接写单个下标`a[100]` 或`a[-100]` 才是索引访问，越界会抛`IndexError`。

> [!WARNING]
> 切片越界和索引越界是两种不同的行为：`a[1:100]` 正常返回能取到的元素，`a[100]` 直接报`IndexError`。判断结果时先看有没有冒号。

### 切片返回新序列

切片得到的是新的序列对象，不是原序列的别名。对列表切片后修改切片，不会改动原列表；但切片只复制最外层，切片里的元素仍与原列表指向同一批对象。

切片是否返回新容器与其中的元素是否被复制是两个层次。容器可变性和别名行为另见[Python对象可变性](/knowledge/cs/programming/python/mutability/)。

## 例子

**用切片取子序列**

```python title="slicing.py"
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

a[2:5]    # [2, 3, 4]
a[:3]     # [0, 1, 2]，前3个
a[-3:]    # [7, 8, 9]，后3个
a[::2]    # [0, 2, 4, 6, 8]，偶数位置
a[::-1]   # [9, 8, ..., 0]，逆序
a[5:2:-1] # [5, 4, 3]，从5向左取到3，不含a[2]
```

`a[5:2:-1]` 的`stop`是`2`，但负步长同样不包含终点，所以取到`a[3]` 为止。

**切片越界与索引越界**

```python title="bounds.py"
a = [0, 1, 2]
print(a[1:10])  # [1, 2]
print(a[10:])   # []
print(a[10])    # IndexError
```

**修改切片不影响原列表**

```python title="slice-copy.py"
a = [1, 2, 3]
b = a[:]
b[0] = 100
print(a)  # [1, 2, 3]
print(b)  # [100, 2, 3]
```

## 练习

1. `a = [0, 1, 2, 3, 4, 5]`，`a[1:5:2]` 的结果是：

   * A.  `[1, 3]`
   * B.  `[1, 2, 3, 4]`
   * C.  `[1, 3, 5]`
   * D.  `[2, 4]`

   答案：A

   从下标`1`开始，到下标`5`之前，每隔`2`个取一个，取到`a[1]` 和`a[3]`。

2. 长度为10的列表`a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`，`a[-3:]` 的结果是：

   * A.  `[7, 8, 9]`
   * B.  `[8, 9]`
   * C.  `[6, 7, 8]`
   * D.  `[]`

   答案：A

   `-3`对应下标`7`，从`7`取到末尾，得到最后3个元素。

3. `a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]`，`a[::-1][0]` 的值是 \_\_\_\_\_\_（value）。

   答案：9

   `a[::-1]` 是逆序列表 `[9, 8, ..., 0]`，再取下标`0`得到`9`。

## 交互探索

交互演示：切片究竟选中了哪些元素？。调整切片起点、终点和步长，观察选中的位置与返回顺序。 请在站点操作演示。
