08 对象引用 可变性 回收

8.1 变量不是盒子

python的变量和c++、java中的引用类似。

>>> a = [1,2,3]
>>> b = a
>>> b.append(4)
>>> a
[1, 2, 3, 4] # a, b两个label实际上贴在了同一个对象上

且在把变量赋给对象之前,对象就已经创建了

>>> class Gizmo:
... def __init__(self):
... print('Gizmo id: %d' % id(self))
...
>>> x = Gizmo()
Gizmo id: 4492646552 # 第一个Gizmo对象
>>> y = Gizmo() * 10
Gizmo id: 4492646384 # 第二个Gizmo对象
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'Gizmo' and 'int'
>>> dir()
['Gizmo', '__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'x'] # Gizmo对象已经创建了,但是没有y

8.2 Identity, Equality and Aliases

每个对象有自己的id,类型和值。id()返回一个对象的id,id的值跟实现有关。CPython中的id就是对象的内存地址。

== 和 is

is比较的是对象的id,is速度更快,且不能被重载。

==调用__eq__(),如a == b实际上是a.__eq__(b)__eq__()的比较内容要看这个类型的具体实现,一般来说是比较对象的值。

元组的不变性

元组的不变性是指的元素元素的数据结构的不变性。元素中的元素的id是不能变的,但是每个元素自身管理的值却可能变化。

>>> t1 = (1,2,[30,40])
>>> t2 = (1,2,[30,40])
>>> t1 == t2
True
>>> id(t1[-1])
4492450696
>>> t1[-1].append(50)
>>> t1
(1, 2, [30, 40, 50])
>>> id(t1[-1])
4492450696 # list的id同上面一样没变
>>> t1 == t2 # t1, t2的值不相同,所以False
False

8.3 拷贝默认是浅拷贝

copy的含义是:创造一个和原对象==为True但是id不同的新对象。

python中用类型的构造函数或者[:](对于序列)可以快速的进行潜拷贝。浅拷贝只会把最外层的对象拷贝一份,但是内层对象还是同一个。

>>> l1 = [1,[2,2]]
>>> l2 = list(l1)
>>> l1 == l2
True
>>> l1[1].append(2)
>>> l1
[1, [2, 2, 2]]
>>> l2
[1, [2, 2, 2]]

copy标准库中有copy和deepcopy可以方便的进行潜拷贝和深拷贝

import copy
class Bus:
def __init__(self, passenger=None):
if passenger is None:
self.passenger = []
else:
self.passenger = list(passenger)
def pick(self, name):
self.passenger.append(name)
def drop(self, name):
self.passenger.remove(name)
if __name__ == '__main__':
bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])
bus2 = copy.copy(bus1)
bus3 = copy.deepcopy(bus1)
print('id(bus1) = {}, id(bus2) = {}, id(bus3) = {}'.format(id(bus1), id(bus2), id(bus3)))
bus1.drop('Bill')
print('bus2.passenger = {}'.format(bus2.passenger))
print('bus3.passenger = {}'.format(bus3.passenger))

输出

id(bus1) = 4432647392, id(bus2) = 4432647616, id(bus3) = 4432649184 # 3个bus都是不同的对象
bus2.passenger = ['Alice', 'Claire', 'David'] # 1和2中的passenger是同一个对象
bus3.passenger = ['Alice', 'Bill', 'Claire', 'David']

deepcopy对拷贝的层数没有限制,可以到无限层。

>>> a = [1,2]
>>> b = [a,3]
>>> a.append(b)
>>> a
[1, 2, [[...], 3]] # a和b循环引用,嵌套了无限层。deepcopy也会拷贝无限层
>>> c = copy.deepcopy(a)
>>> c
[1, 2, [[...], 3]]