在刚学Python的时候就知道元组是不可变的,比如
In [1]: t = (1, 2, 3)
In [2]: id(t)
Out[2]: 4457650000
In [3]: t += (4, 5)
In [4]: id(t)
Out[4]: 4456501496
In [5]: t
Out[5]: (1, 2, 3, 4, 5)
当我们想要改变元组的时候他会新建一个对象来存储值,但是当我们的元组里有一个可变的对象呢
In [8]: t1 = ([1, 2, 3], )
In [9]: t1[0].append(4)
In [10]: t1
Out[10]: ([1, 2, 3, 4],)
其实元组保存的只是对象的引用,如果元组里的元素可变那么元素就是可变的。这里说的不可变只是元组保存的引用不变
在说一下深拷贝、浅拷贝
In [11]: l1 = [1, [2, 3, 4], (5, 6)]
In [12]: l2 = list(l1)
In [13]: l2
Out[13]: [1, [2, 3, 4], (5, 6)]
In [14]: l2[1].append(100)
In [15]: l2
Out[15]: [1, [2, 3, 4, 100], (5, 6)]
In [16]: l1
Out[16]: [1, [2, 3, 4, 100], (5, 6)]
In [18]: l1[-1] += (7, 8)
In [19]: l1
Out[19]: [1, [2, 3, 4, 100], (5, 6, 7, 8)]
In [20]: l2
Out[20]: [1, [2, 3, 4, 100], (5, 6)]
list()方法默认做的是浅拷贝。浅拷贝就是只复制最外层的容器,复制出来容器里的元素仍为源容器中元素的引用。即l2的元素引用还是指向l1的,所以当l2[1]更改了的时候l1[1]也更改了。但是由于元组是不可变元素,所以当更改他的时候会新创建一个元组对象,所以l1和l2中的互不干扰
而深拷贝则不共享引用,全部重新创建新的引用
In [21]: import copy
In [22]: l3 = copy.deepcopy(l1)
In [23]: l3
Out[23]: [1, [2, 3, 4, 100], (5, 6, 7, 8)]
In [24]: l1
Out[24]: [1, [2, 3, 4, 100], (5, 6, 7, 8)]
In [25]: l1[1].append(200)
In [26]: l1
Out[26]: [1, [2, 3, 4, 100, 200], (5, 6, 7, 8)]
In [28]: l3
Out[28]: [1, [2, 3, 4, 100], (5, 6, 7, 8)]
接下来说方法形参问题,都知道形参是一个实参的引用。那么这个时候传不可变对象还好。如果传可变参的话,那么方法做的更改就会关系到方法外
In [29]: def f(a, b):
...: a += b
...: return a
...:
In [30]: x = [1, 2]
In [31]: y = [3, 4]
In [32]: f(x, y)
Out[32]: [1, 2, 3, 4]
In [33]: x
Out[33]: [1, 2, 3, 4]
所以,尽量不要传递一个可变的对象给函数,除非你想这么做
同理,函数的默认参也不要是一个可变对象
class HauntedBus:
def __init__(self, passengers=[]):
self.passengers = passengers
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
运行结果如下
In [32]: bus1 = HauntedBus(['Alice', 'Bill'])
In [33]: bus1.passengers
Out[33]: ['Alice', 'Bill']
In [34]: bus1.pick('Charlie')
In [35]: bus1.drop('Alice')
In [36]: bus1.passengers
Out[36]: ['Bill', 'Charlie']
In [37]: bus2 = HauntedBus()
In [38]: bus2.pick('Carrie')
In [39]: bus2.passengers
Out[39]: ['Carrie']
In [40]: bus3 = HauntedBus()
In [41]: bus3.passengers
Out[41]: ['Carrie']
In [42]: bus3.pick('Dave')
In [43]: bus3.passengers
Out[43]: ['Carrie', 'Dave']
In [44]: bus2.passengers
Out[44]: ['Carrie', 'Dave']
In [45]: bus2.passengers is bus3.passengers
Out[45]: True
In [46]: bus1.passengers
Out[46]: ['Bill', 'Charlie']
可以看到bus2和bus3共用了一组乘客,问题就是出在self.passengers = passengers
,这里如果我们在创建对象时什么也不传则用了默认参passengers=[]
,而默认参是在加载模块的时候定义,不会每次都创建,所以默认参是这个函数的属性,而这个属性是个可变对象的时候,那么修改了之后后续的都会受到影响
网友评论