1. 原生 Python 中的基础索引和切片
在原生 Python
中,可以直接使用索引(Index
)来访问序列(列表、元组、字符串等)中的元素。
支持两种类型的索引:
- 正索引:从左到右,从
0
开始,到(序列长度-1
)结束 - 负索引:从右到左,从
-1
开始,到(-
序列长度) 结束
支持以下两种使用方式:
- 基础索引
- 切片
1.1 基础索引
基础索引也称为简单索引或直接索引,是指通过索引序号直接访问序列中的元素。
基础语法:
sequence[index]
其中:
- sequence: 支持索引操作的数据类型,如列表、元组、字符串等。
- index: 整数,表示元素的位置。
示例 1 ,通过索引访问列表、字符串中的元素:
# 正索引
# 一维列表
arr1d = [1, 2, 3, 4, 5]
# 访问第 2 个元素 → 2
print(arr1d[1])
# 字符串
str_name = "Donald Trump"
# 访问第 3 个元素 → n
print(str_name[2])
# 负索引
# 访问倒数第 2 个元素 → 4
print(arr1d[-2])
# 访问倒数第 3 个元素 → u
print(str_name[-3])
示例 2 ,对于嵌套列表,可以使用多个中括号 []
(链式索引) :
# 二维(3 x 3)
arr2d = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
# 访问第 2 行 → [4, 5, 6]
print(arr2d[1])
# 访问第 2 行、第 3 列 → 6
print(arr2d[1][2])
# 三维(2 x 3 x 3)
arr3d = [[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]],
[[10, 11, 12],
[13, 14, 15],
[16, 17, 18]]]
# 访问第 1 层、第 2 行 → [4, 5, 6]
print(arr3d[0][1])
# 访问第 1 层、第 2 行、第 3 列 → 6
print(arr3d[0][1][2])
1.2 切片
切片(Slicing
) 是一种通过指定起始、终止位置和步长来获取序列类型(如列表、字符串、元组等)子集的操作。
基本语法:
sequence[start : end : step]
参数说明:
start
:起始索引(包含该位置元素),指定切片的起始位置,默认为0
。end
:结束索引(不包含该位置元素),指定切片的结束位置,默认到序列末尾结束。step
:步长,控制元素选取的间隔方向和步幅,支持正数(从左向右选取)和负数(从右向左选取),默认为1
。
示例 1 ,一维的嵌套列表:
arr = [0, 1, 2, 3, 4]
print(arr[::2]) # [0, 2, 4](步长2,正向)
print(arr[::-1]) # [4, 3, 2, 1, 0](反向)
print(arr[3:0:-1]) # [3, 2, 1](反向,start=3, end=0)
对于二维及以上的 Python
原生嵌套列表,无法直接进行多个维度的切片操作。当尝试使用 []
进行多维切片时,只能访问到第一个维度的元素,而无法同时处理多个维度。
示例 2 ,单个 []
时表示在第一个维度进行切片:
# 二维(3 x 3)
arr2d = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
# 第一行和第三行
print(arr2d[0::2])
# 输出: [[1, 2, 3],
# [7, 8, 9]]
示例 3 ,单个 []
输入多个维度时不支持:
# 期望:
# 行方向:第一行和第三行,列方向:第一列和第三列
print(arr2d[0::2,0::2])
# 报错: TypeError: list indices must be integers or slices, not tuple
示例 4 ,多个 []
时并不是依次对每个维度进行切片操作,始终都只能在第一个维度上执行操作:
# 期望:先选择第一行和第三行,再选择第一列和第三列
# 期望输出:
# [[1 3]
# [7 9]]
print(arr2d[0::2][0::2]) # 实际输出: [[1, 2, 3]] ,先选择第一行和第三行(剩两行),再选择第一行和第三行(剩一行)
# 三维(2 x 3 x 3)
arr3d = [[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]],
[[10, 11, 12],
[13, 14, 15],
[16, 17, 18]]]
# 期望:先第一层和第三层,再第一行和第三行,最后第一列和第三列
# 期望输出:
# [[[1 3]
# [7 9]]]
print(arr3d[0::2][0::2][0::2]) # 实际输出: [[[1, 2, 3], [4, 5, 6], [7, 8, 9]]] ,先选择第一层和第三层(只有第一层),之后都是选择第一层
示例 5 ,sequence[:]
、sequence[::]
表示完整切片(全切),等价于 sequence[0:len(sequence):1]
:
arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 全切:选择所有行和所有列
print(arr[::])
2. NumPy 基础索引
NumPy
数组支持比 Python
原生列表更强大的索引功能,根据索引对象的类型划分了三种方式:
- 基础索引:只使用索引序号或切片。
- 高级索引:使用整数数组或布尔数组。
- 字段访问:通过字段名访问(结构化数组)。
注意:基础索引包含了原生 Python
中的基础索引和切片,并支持多维操作。
2.1 只使用索引序号
与原生 Python
序列类型一样,使用中括号加下标([index]
)的方式,返回的是原数组的视图(View
),对索引结果的修改会直接影响原数组,也支持正负索引。
索引规则:
- 每个
[]
对应一个维度的操作 - 索引顺序从外到内
- 未指定的维度默认包含所有元素
示例 1 ,使用 [index]
获取单个元素:
# 一维数组
arr1d = np.array([1, 2, 3, 4, 5])
# 访问第 2 个元素 → 2
print(arr1d[1])
# 二维数组(3 x 3)
arr2d = np.array(
[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 访问第 2 行、第 3 列 → 6
print(arr2d[1][2])
# 三维数组(2 x 3 x 3)
arr3d = np.array(
[[[1, 2, 3],
[4, 5, 6],
[7, 8, 9]],
[[10, 11, 12],
[13, 14, 15],
[16, 17, 18]]])
# 访问第 1 层、第 2 行、第 3 列 → 6
print(arr3d[0][1][2])
上面案例中对于多维数组的索引操作,我们使用了多个中括号([]
),将每个维度的索引分别按照维度顺序(从 0
轴开始)放在各自的方括号中,例如一个三维数组 arr[0][1][2])
表示:
[0]
:第一个轴(最外层)上的第0
个(定位到二维数组)。[1]
:第二个轴(中层)上的第1
个(定位到行)。[2]
:第三个轴(最内层)上的第2
个(定位到行的第几列)。
示例 2 ,当 []
数量小于轴数时,未指定的维度默认包含所有元素 :
# 第一层
print(arr3d[0]) # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 第一层第二行
print(arr3d[0][1]) # [4, 5, 6]
以上使用方式和原生 Python
一致,此外 NumPy
还支持使用一个中括号将多个维度的索引放在一个中括号中,即支持以下两种表达式:
arr[i][j]...[k]
:使用多个中括号arr[i,j...k]
:使用一个中括号(推荐)
示例 3 ,使用一个中括号操作三维数组:
# 第一层
print(arr3d[0]) # [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# 第一层第二行
print(arr3d[0, 1]) # [4, 5, 6]
# 第一层第二行第三列
print(arr3d[0, 1, 2]) # 6
2.2 切片
NumPy
切片语法将原生 Python
的切片概念扩展到多维数组,基本语法:
arr[start:end:step, start:end:step, ...]
注意事项:
- 使用多个
[]
时和原生Python
一样,只能在第一个维度上进行操作。 - 使用逗号分隔不同维度,顺序对应数组的轴(从
0
开始),每个维度都遵循start:end:step
规则。 - 未指定的维度默认包含所有元素。
- 支持负索引和自动越界处理。
- 生成的所有数组始终是原数组的视图,与原数组共享数据内存,修改视图会影响原数组。
- 从大数组中提取切片时需要特别小心,因为提取的小部分包含对原大数组的引用,而大数组的内存不会被释放,直到所有派生的数组都被垃圾回收。
示例 1 ,一维数组:
arr1d = np.array([1, 2, 3, 4, 5])
# 索引区间: [1,4)
print(arr1d[1:4]) # [2 3 4]
# 开始、结束默认值,步长为 2
print(arr1d[::2]) # [1 3 5]
# 负索引,步长为 1(反向)
print(arr1d[::-1]) # [5 4 3 2 1]
二维数组的形状为【行 x 列】 ,则切片格式为 arr[start:end:step, start:end:step]
(其他维度的数组,参考二维进行类推即可):
- 第一个参数:对行进行操作
- 第二个参数:对列进行操作
示例 2 ,二维数组:
arr2d = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
# 先取所有行,再取第 2 列:
print(arr2d[:, 1]) # [2, 5, 8]
# 取第 2 行到末尾,再取第 2 列到末尾
print(arr2d[1:, 1:]) # [[5, 6], [8, 9]]
# 先每隔一行取一次,再取所有列
print(arr2d[::2, :]) # [[1, 2, 3], [7, 8, 9]]
示例 3 ,三维数组:
arr3d = np.array([
[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
[[10, 11, 12], [13, 14, 15], [16, 17, 18]]
])
# 取第 1 层,所有行,第 2 到 3 列
print(arr3d[0, :, 1:3]) # [[2 3] [5 6] [8 9]]
# 取所有层,第 2 行,第 1 列
print(arr3d[:, 1, 0]) # [ 4 13]
示例 4 ,未指定的维度默认包含所有元素 :
# 第 1 、3 层的所有行所有列
print(arr3d[::2])
# [[[1 2 3]
# [4 5 6]
# [7 8 9]]]
# 第 1 、3 层,再第 1 、3 行的所有列
print(arr3d[::2, ::2])
# [[[1 2 3]
# [7 8 9]]]
示例 5 ,可以使用冒号(:
)或(::
)显式的指定某个维度的全部元素:
# 二维数组:
arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
# 取所有元素
print(arr[::, ::])
# 等价写法:print(arr[:, :]) print(arr[:])
# 输出:
# [[1 2 3]
# [4 5 6]
# [7 8 9]]
# 取所有行,第 1、2 列
print(arr[::, :2:1])
示例 6 ,除此之外,NumPy
还支持使用省略号(Ellipsis
)进行自动扩展,隐式补全所有未指定的维度,进一步简化操作:
# 三维数组
arr_3d = np.arange(24).reshape(2, 3, 4) # 形状 (2,3,4)
# 全切:所有元素
print(arr_3d[...])
# [[[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
#
# [[12 13 14 15]
# [16 17 18 19]
# [20 21 22 23]]]
# 所有层、所有行、第 1、2 列
print(arr_3d[..., 0:2:1])
# [[[ 0 1]
# [ 4 5]
# [ 8 9]]
#
# [[12 13]
# [16 17]
# [20 21]]]
注意,一个数组只能写一个省略号,多个会报错 IndexError
:
arr_3d[..., 0,...,] # IndexError: an index can only have a single ellipsis ('...')