Python中mutable与immutable和二维列表的初始化问题
初始化一个shape为(3,3)的二维列表,正确的写法是这样的:
a = [[None] * 3 for i in range(3)]
print(a)
a[0][0] = 1
print(a)
#经过测试没有问题
# 输出:
#[[None, None, None], [None, None, None], [None, None, None]]
#[[1, None, None], [None, None, None], [None, None, None]]
但是,如果写成这样:
a = [[None] * 3] * 3
print(a)
# 输出 : [[None, None, None], [None, None, None], [None, None, None]]
看起来像是没有问题,实际上,如果进行赋值操作,就会发现问题:
a[0][0] = 1
print(a)
# 输出:[[1, None, None], [1, None, None], [1, None, None]]
结果三行的对应位置的元素全都改变了!(就因为这个问题找了好长时间的bug… =_=|||)
后来知道,这个问题与Python中数据类型的分类有关,所以在这里记录一下,存个档。
步入正题,在Python中有一句话:万物皆对象(好像OOP都是这么说。。),在Python中所写的一切都可以看作是一个对象,比如定义的整数、浮点数以及列表,元组等,但是这些对象也有区别,这些对象,有些属于mutable(可变),有些则是属于immutable(不可变),这是什么意思呢?先看一个例子:
# 这里定义一个整数 id()函数可以查看对象的内存位置
a = 3
print(id(a))
a += 1
print(id(a))
#输出:
# 94034634295264
# 94034634295296
# 接下来定义一个列表:
a = [1,2,3]
print(id(a))
a[0] = 100
print(id(a))
# 输出:
# 139637076845448
# 139637076845448
通过对比,不难发现,对数值,比如整数、浮点数进行改变,内存的位置会发生改变,但是对列表,字典之类的进行改变,则不会改变内存位置。
实际上,对于整数、浮点数,改变值的时候,实际上是新开辟了一块内存,然后把加法的结果填充到这快内存中,然后再把a指向这块新的内存。
这里提到了“指向”,如果学过C或者C++,可能会想到指针或者引用,再Python中也有说引用的,那么这里就可以看出两种语言的“引用”是不同的了,C++中,对引用的数据进行改变,这个位置应该是不变的,而再Python中,显然,就像上面,相当于改变了“引用”,而不是内存的数据。
这里可能有点绕,不过这里也就是所说的mutable和immutable的问题了,在Python中,immutable的类型有整数、浮点数、字符串以及元组等等,而mutable的类型有list,dict以及自定义的类型。
那么回到上面一开始的问题中,知道了list是mutable的,所以在创建的时候,并不是赋值到了新的位置,只是创建了三个指针(a[0], a[1], a[2])指向了同一块内存而已,所以,改变其中一个的某个元素,会对所有的元素产生影响。