目录

【Python】Python中List初始化和切片的雷区

列表的初始化

  • 使用*对列表进行复制初始化,实际上复制的是被复制元素的引用
# 元素为数字(安全)
test = [0]*3 # test = [0,0,0]
test[0] = 1  # test变为[1,0,0]
# 元素为列表(不安全)
test = [[0]]*3 # test = [[0],[0],[0]]
test[0][0] = 1 # test变为[[1],[1],[1]],这个操作更改了元素指向的列表内的值
test[0] = [0] # test变为[[0],[1],[1]],这个操作直接改变了元素的指向

使用test = [0]*3test数组进行初始化是安全的,更改某一个元素时会替换该元素的值(改变指向)。

但使用test = [[0]]*3test数组进行初始化时,由于每个元素指向相同的列表对象,更改某个元素指向的列表里面的值时,所有的元素都会一起变化。

因此,创建多维列表时,为了避免上述情况,除第一维度之外均应该使用列表推导创建:

# 创建3*3二维全零列表
test = [[0]*3 for _ in range(3)]
# 创建3*3*3三维全零列表
test = [[[0]*3 for _ in range(3)] for _ in range(3)]

列表的切片(和np.ndarray区别)

列表切片和取索引

列表list中的切片和取索引是嵌套操作

# list嵌套切片、取索引的区别
test = [[0]*3 for _ in range(3)]
'''
test = [[0,0,0],
        [0,0,0],
        [0,0,0]]
'''
# list嵌套切片
block1 = test[0:2] # block1 = [[0,0,0],[0,0,0]]
block2 = test[0:2][0:1] # block2 = [[0,0,0]]
# list取索引
block3 = test[0] # block3 = [0,0,0]
block4 = test[0][0:2] # block4 = [0,0]

可以看到,list的切片和取索引是执行从左到右连续的操作

要理解list的切片语法,只要把每个切片和取索引看作流水线操作,从左到右,每次用前一次操作得到的结果来进行当前操作,最终得到结果。

numpy.ndarray的切片和取索引操作

list的切片和取索引操作不同,ndarray对象的切片语法和MatLab的语法更相似。

ndarray对象的切片按每个维度切片,每个维度用逗号隔开。

import numpy as np
list_nparr = np.array([[1,2,3],[4,5,6],[7,8,9]])
# 切片
new_arr = list_nparr[0:2,0:2] # new_arr = np.array([[1,2],[4,5]])

更多内容见:Numpy教程:索引与切片

list实现分维度切片

若需要用list实现和ndarray相似的按维度切片,则需要每次取一个维度切片后再进行组合。

import numpy as np
list_test = [[1,2,3],[4,5,6],[7,8,9]]
list_nparr = np.array(list_test)
# ndarray切片
new_arr = list_nparr[0:2,0:2] # new_arr = np.array([[1,2],[4,5]])
# list切片
new_list = [list_test[i][0:2] for i in range(2)] # new_list = [[1,2],[4,5]]