在复制list
时,默认是做了浅拷贝(shallow copy),也就是说复制了最外层垯容器,而内部元素则是原容器的引用。这么做在内部元素都是不可变类型的时候没有问题,但是在内部包含可变元素的时候会发生一些意想不到的情况:
>>> l1 = [3, [66, 55, 44], (7, 8, 9)]
>>> l2 = list(l1)
>>> l1.append(100)
>>> l1[1].remove(55)
>>> print('l1:', l1)
l1: [3, [66, 44], (7, 8, 9), 100]
>>> print('l2:', l2)
l2: [3, [66, 44], (7, 8, 9)]
>>> l2[1] += [33, 22]
>>> l2[2] += (10, 11)
>>> print('l1:', l1)
l1: [3, [66, 44, 33, 22], (7, 8, 9), 100]
>>> print('l2:', l2)
l2: [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]
所以,当容器中包含可变元素的时候,为了避免意想不到的情况发生,推荐使用深拷贝,即copy.deepcopy
。
在Python
中的函数传参是共享传参(call by sharing),即函数形参获得实参中各个引用的副本。带来的副作用是函数内部的操作可能会影响到可变类型的实参。
>>> 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))
在Python
默认解释器CPython
中,默认使用引用计数来做垃圾回收。每个对象维护一个变量,计算自己被引用的次数,当这个值归零之后,对象被销毁,执行__del__
方法。对于有循环引用的对象,则使用分代垃圾回收算法。
按照上面的说法,引用数归零之后对象被垃圾回收。有时需要引用对象而不想让它存活超过所需时间,比如缓存。
这时候就需要引入弱引用的概念。
>>> import weakref
>>> a_set = {0, 1}
>>> wref = weakref.ref(a_set)
>>> wref
>>> wref()
{0, 1}
>>> a_set = {2, 3, 4}
>>> wref()
{0, 1}
>>> wref() is None
False
>>> wref() is None
True
下面演示 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
...
>>> sorted(stock.keys())
['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']
>>> del catalog
>>> sorted(stock.keys())
['Parmesan']
>>> del cheese
>>> sorted(stock.keys())
[]