本章的内容有点枯燥,但是这些话题却是解决Python程序中很多不易察觉的bug的关键。
变量
名称不是对象,而是单独的东西。
变量是标注,而不是盒子。
对引用式变量来说,说把「变量」分配给「对象」更合理,反过来说就有问题。因为对象在赋值前就创建了。
Python中赋值语句,应该始终先读右边。对象在右边创建或获取,在此之后左边的变量才会绑定到对象上,这就像为对象贴上标注。
别名
每个变量都有「标识」、「类型」、「值」。
对象一旦创建它的标识绝不会变。标识可以理解为对象在内存中的地址。
==和is
==:比较两个对象的值
is:比较对象的标识id()
is运算符比==速度快,因为它不能重载。
元祖的相对不可变性
元祖的不可变是指tuple数据结构的物理内容(保存的引用)不可变,于引用的对象无关。
浅复制
l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = list(l1)
l1.append(100)
l1[1].remove(55)
print(f'l1:{l1}') # l1:[3, [66, 44], (7, 8, 9), 100]
print(f'l2:{l2}') # l2:[3, [66, 44], (7, 8, 9)]
l2[1] += [33, 22]
l2[2] += (10, 11)
print(f'l1:{l1}') # l1:[3, [66, 44, 33, 22], (7, 8, 9), 100]
print(f'l2:{l2}') # l2:[3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]
l1中使用append添加后l2不会同步添加,因为l2重新拷贝了一份列表
从下图可以看出,定义l2后将l1拷贝了一份,但是其中第二位和第三位还是引用了同一份数据
修改l2的元祖之后,它重新指向了一个新的元祖
深复制
import copy
copy.deepcopy() # 深复制
copy.copy() # 浅复制
import copy
l1 = [3, [66, 55, 44], (7, 8, 9)]
l2 = copy.deepcopy(l1)
l1.append(100)
l1[1].remove(55)
print(f'l1:{l1}') # l1:[3, [66, 44], (7, 8, 9), 100]
print(f'l2:{l2}') # l2:[3, [66, 55, 44], (7, 8, 9)]
l2[1] += [33, 22]
l2[2] += (10, 11)
print(f'l1:{l1}') # l1:[3, [66, 44], (7, 8, 9), 100]
print(f'l2:{l2}') # l2:[3, [66, 55, 44, 33, 22], (7, 8, 9, 10, 11)]
函数的默认值
注意:不要使用可变类型作为参数的默认值,通常使用None作为接收可变值的默认值。
del和垃圾回收
对象绝不会自行销毁;然而,无法得到对象时,可能会被当作垃圾回收。
del语句删除名称,而不是对象。
del命令可能会导致对象被当作垃圾回收,但是仅当删除的变量保存的是对象的最后一个引用,或者无法得到对象时。
所以,当一个对象没有任何引用,无法得到它的时候,它就会被回收。
引用计数:每个对象都会统计有多少引用指向自己,当引用计数归零时,对象立即就被销毁。
weakref 弱引用
弱引用的主要用途是实现保存大对象的高速缓存或映射,但又不希望大对象仅仅因为它出现在高速缓存或映射中而保持存活。
weakref.ref(object[, callback])
返回对 对象 的弱引用。如果原始对象仍然存活,则可以通过调用引用对象来检索原始对象;如果引用的原始对象不再存在,则调用引用对象将得到 None
。
接受一个可选的回调函数,删除所引用的对象时会调用这个函数。
weakref.WeakKeyDictionary(dict)
弱引用键的映射类。当不再存在对键的强引用时,字典中的条目将被丢弃。这可被用来将额外数据关联到一个应用中其他部分所拥有的对象而无需在那些对象中添加属性。这对于重载了属性访问的对象来说特别有用。
weakref.WeakValueDictionary(dict)
弱引用值的映射类。当不再存在对该值的强引用时,字典中的条目将被丢弃。
weakref.proxy(object[, callback])
返回 object 的一个使用弱引用的代理。它可以和object一样使用