fluent Python 读书笔记 对象引用、可变性、垃圾回收

8.1变量不是盒子

既然变量不应该被理解为盒子,那应该理解为什么?
理解为一个小标签纸,就是女生常用来贴文件的那种。
我可以给一个对象贴多个标签纸,他们是这个对象的别名,当它们共同指向一个可变对象时,改变一个另一个也会变。
另外结合最近C学的,其实我们在PYthon里的数组也就是指针嘛,为什么我们改别名对象也会变,就是因为你改别名
就是改了那个地址里存的数据。
a = [1, 2, 3]
b = a
a.append(4)
print(b)
在上面这个示例中,b和a的id都是一样的, b is a 的结果(比较的是它们的地址)是True。
以上情况对字典也一样。

a = [1, 2, 3]
b = [1, 2, 3]
则与以上示例完全不同


这里再讲一些细节。当我们写出a==b时, 实际上解释器会这样解释:a.__eq__(b), 所有对象都是继承object的,
而object里的eq其实比较的是地址。不过多数数据类型都把这个方法override了。

8.2.2
元组真的是不可变的吗?
可以这样说,也可以不这样说。
我们在元组里保存的是对象的引用,真正不可变是对象的引用,也就是a[i]指向的那一片地方的地址是不变的。
看下面这个例子
t1 = (1, 2, 3, [30, 40])
print(id(t1[-1]))
t1[-1].append(50) //成功!
print(id(t1[-1]))
//两次id没变,所以不变的是引用

8.3默认做浅复制
python里的复制其实默认是浅复制
什么叫浅复制?
l1 = [3, [55, 44], (7, 8, 9)]
l2 = list(l1) //或者 l2 = l1[:]
这个时候l2 与 l1的id是不一样的,因为l2你相当于新开了另一块空间,但是,对于其中的元素,你复制的是他们的引用
而并没有新建。也就是说这里改l1[1],l2[1]也会跟着变!他们互为别名,指向同一块地址。
还有一个细节。当我们用l2[1] += [66]时, l1[1]会跟着变,因为对于可变对象,+=相当于就地修改
而l2[2] += (66,55), l1[2]不会变,因为对于不可变对象, +=会创造一个新对象,导致l2丢失了l1[2]的引用

假设我们这里建校巴,Bus类有一个list属性包含乘客。
import copy
bus1 = Bus()
bus2 = copy.copy(bus1)
bus3 = copy.deepcopy(bus1)
浅复制新对象地址与原来是不同的
所以bus2 的id 与 bus1的id不同
当然 bus1, bus2, bus3的id都不同
但是bus2里的那个list属性和bus1是共享的,这是浅复制的特点。
你改bus2.passengers//是个list,bus1.passengers也会变
但深复制就不同。

a = [10, 20]
b = [a, 20]
a.append(b)
print(a)
接下来看看比较有趣的循环引用会是一个无限循环

8.4共享传参
我们的函数的内部的形参,其实是实参的别名。
所以,对于可变对象,我们可以在函数内部修改它,但不能改变它的对象。
也即 涉及到赋值时,会创建新对象,这个新对象就与原来的不同了。



接下来说明一下以可变对象作为函数参数默认值有什么不好
class HauntedBus:
    def __init__(self, passengers=[]):
        self.passengers = passengers

如果我们这样写这个类,那么这个默认值[]会在模块加载时就生成,而self.passengers
和passengers就是这个空列表的别名。这样当你不加参数创建2个HauntedBus实例时,其实你改其中一个的属性
另一个的属性也会被改。
所以,我们对于可变属性,默认值最好设为None
class HauntedBus:
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = passengers
这样写也不太好,因为self.passengers还是passengers的别名,这样的话,当我们改一个实例的属性时
另一个实例还是有可能受到影响。
所以改为:
class HauntedBus:
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = list(passengers)


对于弱应用会跳过
主要这里总结一下Python对象删除的机制吧。什么情况下python对象才会被真正销毁?答,没有引用指向它时,也就是说
它的别名都被删除del了。


猜你喜欢

转载自blog.csdn.net/weixin_41958153/article/details/80629194
今日推荐