元组的相对不可变性
>>> t1 = (1, 2, [30, 40])
>>> t2 = (1, 2, [30, 40])
>>> t1 == t2
True
>>> id(t1[-1])
4302515784
>>> t1[-1].append(99)
>>> t1
(1, 2, [30, 40, 99])
>>> id(t1[-1])
4302515784
>>> t1 == t2
False
默认浅复制
>>> l1 = [3, [66, 55, 44], (7, 8, 9)]
>>> l2 = list(l1)
>>> l1.append(100)
>>> l1[1].remove(55)
>>> l1
[3, [66, 44], (7, 8, 9), 100]
>>> l2
[3, [66, 44], (7, 8, 9)]
>>> l2[1] += [33, 22]
>>> l2[2] += (10, 11)
>>> l1
[3, [66, 44, 33, 22], (7, 8, 9), 100]
>>> l2
[3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]
使用 copy 模块 deepcopy 和 copy 函数为任意对象做深复制和浅复制
class Bus:
def __init__(self, passengers=None):
if passengers is None:
self.passengers = []
else:
self.passengers = list(passengers)
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
>>> from Bus import Bus
>>> import copy
>>> bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])
>>> bus2 = copy.copy(bus1)
>>> bus3 = copy.deepcopy(bus1)
>>> id(bus1), id(bus2), id(bus3)
(49126312, 48367432, 49224616)
>>> bus1.drop('Bill')
>>> bus2.passengers
['Alice', 'Claire', 'David']
>>> id(bus1.passengers), id(bus2.passengers), id(bus3.passengers)
(49175944, 49175944, 49044552)
>>> bus3.passengers
['Alice', 'Bill', 'Claire', 'David']
可以实现特殊方法 __copy__() 和 __deepcopy__(), 控制 copy 和 deepcopy 的行为。
函数的参数为引用
>>> def f(a, b):
a += b
return a
>>> x = 1
>>> y = 2
>>> f(x, y)
3
>>> x, y
(1, 2)
>>> a = [1, 2]
>>> b = [3, 4]
>>> f(a, b)
[1, 2, 3, 4]
>>> a, b
([1, 2, 3, 4], [3, 4])
>>> t = (10, 20)
>>> u = (30, 40)
>>> f(t, u)
(10, 20, 30, 40)
>>> t, u
((10, 20), (30, 40))
不要使用可变类型作为参数的默认值
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)
>>> from HauntedBus import HauntedBus
>>> bus1 = HauntedBus(['Alice', 'Bill'])
>>> bus1.passengers
['Alice', 'Bill']
>>> bus1.pick('Charlie')
>>> bus1.drop('Alice')
>>> bus1.passengers
['Bill', 'Charlie']
>>>
>>> bus2 = HauntedBus()
>>> bus2.pick('Carrie')
>>> bus2.passengers
['Carrie']
>>> bus3 = HauntedBus()
>>> bus3.passengers
['Carrie']
>>> bus3.pick('Dave')
>>> bus2.passengers
['Carrie', 'Dave']
>>> bus2.passengers is bus3.passengers
True
>>> bus1.passengers
['Bill', 'Charlie']
del 与垃圾回收
del 语句删除名称,而不是对象。del 命令可能会导致对象被当作垃圾回收,但是仅当删除的变量保存的是对象的最后一个引用,或者无法得到对象时。重新绑定也可能会导致对象的引用数量归零,导致对象被销毁。
销毁实例时,Python 解释器会调用 __del__ 方法。
使用 weakref.finalize 注册一个回调函数, 在销毁对象时调用。
>>> import weakref
>>> s1 = {1, 2, 3}
>>> s2 = s1
>>> def bye():
print('bye...')
>>> ender = weakref.finalize(s1, bye)
>>> ender.alive
True
>>> del s1
>>> ender.alive
True
>>> s2 = 'abc'
bye...
>>> ender.alive
False
弱引用
弱引用不会增加对象的引用数量。 引用的目标对象称为所指对象 (referent) 。 因此, 弱引用不会妨碍所指对象被当作垃圾回收。
>>> import weakref
>>> a_set = {0, 1}
>>> wref = weakref.ref(a_set)
>>> wref
weakref at 0x0000000002EE6598; to 'set' at 0x0000000002EC2AC8
>>> wref()
{0, 1}
>>> a_set = {2, 3, 4}
>>> wref()
{0, 1}
>>> wref() is None
False
>>> wref() is None
True
1. 调用 wref() 返回的是被引用的对象, {0, 1}。 因为这是控制台会话, 所以 {0, 1} 会绑定给 _ 变量。
2. a_set 不再指代 {0, 1} 集合,因此集合的引用数量减少了。但是 _ 变量仍然指代它。
3. 计算这个表达式时,{0, 1} 存在,因此 wref() 不是 None。但是,随后 _ 绑定到结果值 False。现在 {0, 1} 没有强引用了。
WeakValueDictionary 类实现的是一种可变映射,里面的值是对象的弱引用。
class Cheese:
def __init__(self, kind):
self.kind = kind
def __repr__(self):
return 'Cheese(%r)' % self.kind
>>> import weakref
>>> stock = weakref.WeakValueDictionary()
>>> catalog = [Cheese('Red Leicester'), Cheese('Tilsit'),
... Cheese('Brie'), Cheese('Parmesan')]
...
>>> for cheese in catalog:
... stock[cheese.kind] = cheese
...
3
3
4
4>>> sorted(stock.keys())
['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']
>>> del catalog
>>> sorted(stock.keys())
['Parmesan']
>>> del cheese
>>> sorted(stock.keys())
[]
弱引用的局限
基本的 list 和 dict 实例不能作为弱引用的目标(或称所指对象), 但是它们的子类可以。
class MyList(list):
"""list的子类, 实例可以作为弱引用的目标"""
a_list = MyList(range(10))
# a_list可以作为弱引用的目标
wref_to_a_list = weakref.ref(a_list)
注意:
在 Python 控制台中输入 help(tuple), 你会看到这句话: 如果参数是一个元组, 那么返回值是同一个对象。
对元组 t 来说, t[:] 不创建副本, 而是返回同一个对 象的引用。 此外, tuple(t) 获得的也是同一个元组的引用。
>>> t1 = (1, 2, 3)
>>> t2 = tuple(t1)
>>> t2 is t1
True
>>> t3 = t1[:]
>>> t3 is t1
True
字符串字面量可能会创建共享的对象
>>> t1 = (1, 2, 3)
>>> t3 = (1, 2, 3)
>>> t3 is t1
False
>>> s1 = 'ABC'
>>> s2 = 'ABC'
>>> s2 is s1
True
共享字符串字面量是一种优化措施, 称为驻留(interning)