@IT·互联网首页推荐程序员

Python - 记学习对象可变性

2017-07-20  本文已影响174人  c37d344afd22

在刚学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=[],而默认参是在加载模块的时候定义,不会每次都创建,所以默认参是这个函数的属性,而这个属性是个可变对象的时候,那么修改了之后后续的都会受到影响


最后

爱生活,爱小丽

上一篇 下一篇

猜你喜欢

热点阅读