众所周知,tuple与list的区别就是tuple是不可变的,是为了让程序更安全,tuple可以当作dict的key但是list不行。但tuple并不是完全不可变的,如下所示,tuple中的list的元素就是可以修改的。
>>> a = 1
>>> b = 2
>>> c = [3,4,5]
>>> d = (a,b,c)
>>> d
(1, 2, [3, 4, 5])
>>> d[2][0]=9
>>> d
(1, 2, [9, 4, 5])
首先我们需要了解python中list的一些特性:
>>> c
[9, 4, 5]
>>> c2 = c # 使用=定义一个新的list,实际上c2只是c的一个引用,修改c2中的元素,c也会同时被修改
>>> c2
[9, 4, 5]
>>> c2[0]= 99
>>> c
[99, 4, 5]
>>> id(c)
1868306709960
>>> id(c2) # 发现c2和c的id是一样的,即二者的地址相同
1868306709960
>>> c3 = [99, 4, 5] # 定义一个新的c3,元素组成与c2一样,不过此时虽然二者内容相等,但却是不同的list对象,占用的内存地址不同
>>> c3 == c2 # == 只判断值是否相等
True
>>> c3 is c2 # is判断内存地址是否相同
False
>>> id(c3) # 发现c3的id与c2不同
1868306685576
>>> id(c3[0]) # 查看c3里第一个元素的id
1885009424
>>> c3[0]=999
>>> id(c3[0]) # 修改c3[0]的值后,其id也随之改变,但是c3的地址不会发生改变
1868273844016
>>> id(c3)
1868306685576
看完以上的代码,我们基本就能明白tuple中list中元素可变的原因:虽然list中的元素变了,但是list对象本身的内存地址并没有变,所以对tuple来说并没有元素发生变化。那么能否将list整体替换呢?理论上是不可行的,因为替换list本身会修改对象内存地址。看以下演示
>>> d
(1, 2, [99, 4, 5])
>>> d[2] = 3 # 不可将list整体替换
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> d[2]=c2 # 即使是将list整体替换成内存地址相同的对象也不可以,python直接不允许这种操作,以避免错误操作
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
c++里的参数传递允许传递引用和指针(所以C++难呀),而在python里,参数的传递是传递对象引用。
在python中,不可改变的对象有:str,int,float,tuple;可以改变的对象有list,dict,set。可变和不可变实际上指的是对象所占用的内存地址是否可变。下面以str和list演示可变和不可变的区别。
>>> x = 'abc'
>>> type(x)
<class 'str'>
>>> y = ['a', 'b', 'c']
>>> type(y)
<class 'list'>
>>> x2 = 'abc' # 定义一个新的x2
>>> y2 = ['a', 'b', 'c']
>>> id(x), id(x2) # 比较两个内容相同的str的id,发现他们所占用的内存地址相同,
(1868273892576, 1868273892576)
>>> id(y), id(y2) # 比较两个内容相同的list的id,发现不同
(1868306709832, 1868306709768)
>>> x3 = x # 将x值赋给x3,此时二者占用的是同一个内存地址,相当于引用
>>> x3
'abc'
>>> x3 += 'd' # 修改x3时,由于x是str类型不可变,所以x并不会跟着变,x3此时实际上是调用了新的内存地址
>>> x3
'abcd'
>>> x
'abc'
>>> y3 = y # 对于list正相反
>>> y3
['a', 'b', 'c']
>>> y3.append('d')
>>> y3
['a', 'b', 'c', 'd']
>>> y
['a', 'b', 'c', 'd']
>>> y2
['a', 'b', 'c']
int和float类型和str一样,dict和list一样,不过tuple虽然是不可变的,但和str不完全一样:
>>> n = (1,2,3)
>>> m = (1,2,3)
>>> id(n), id(m) # 虽然tuple是不可变的,但是内容相同的两个tuple地址是不一样的,这和list类似。
(1868306704208, 1868306704424)