【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]*3
对test
数组进行初始化是安全的,更改某一个元素时会替换该元素的值(改变指向)。
但使用test = [[0]]*3
对test
数组进行初始化时,由于每个元素指向相同的列表对象,更改某个元素指向的列表里面的值时,所有的元素都会一起变化。
因此,创建多维列表时,为了避免上述情况,除第一维度之外均应该使用列表推导创建:
# 创建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]]