Variables Are Not Boxes
这个比喻非常棒。
Identity, Equality, and Aliases
a is b
对比的是id(a) == id(b)
,id
函数不能重载,在 CPython 里面指的是变量所在的内存地址(在其他实现里可能不一样)a == b
对比的是a.__eq__(b)
Copies Are Shallow by Default
浅复制与深复制。list(l)
/ l[:]
这种是浅复制;深复制由 copy.deepcopy
提供。如果想要有自定义的复制实现,可以实现 __copy__()
/ __deepcopy__()
函数。参考 copy
模块的文档。
Function Parameters as References
关于 Python 调用函数时的参数传递机制,可以用 call by sharing 来表达。即是说:
The parameters inside the function become aliases of the actual arguments.
这意味着如果函数参数是可变类型时,在函数内对其进行操作,会影响函数外的变量:
>>> def f(a, b):
... a += b
... return a
...
>>> a = [1, 2]
>>> b = [3, 4]
>>> f(a, b)
[1, 2, 3, 4]
>>> a, b
([1, 2, 3, 4], [3, 4])
Mutable Types as Parameter Defaults: Bad Idea
函数的参数默认值,不应该是可变类型,因为会被不同的函数实例共享:
>>> def append_f(l=[]):
... l.append('1')
... return l
...
>>> append_f(['0'])
['0', '1'] # 正常
>>> append_f()
['1'] # 正常
>>> append_f()
['1', '1'] # 不正常
Defensive Programming with Mutable Parameters
call by sharing 使得如果你的函数接受一个可变类型的参数时,你需要跟调用方协定,是否可以改变参数指向的 object 的值。如果你不想改变参数的值,那么考虑把参数复制一份到函数内部的作用域中:
def Bus:
def __init__(self, passengers=None):
if passengers is None:
self.passengers = []
else:
self.passengers = list(passengers) # 复制一份
del and Garbage Collection
CPython 使用的 GC 是引用计数。当一个对象不再能被访问到时,它的引用计数会变成 0,然后马上被垃圾回收
。del
可以解除一个变量与其关联的对象的绑定,这样会使该对象的引用计数减少。
Weak References
Weak references to an object do not increase its reference count. The object that is the target of a reference is called the referent. Therefore, we say that a weak reference does not prevent the referent from being garbage collected.
weakref 模块封装了一些操作 weak reference 的方法。根据它的文档,weakref 的常用场景是:
A primary use for weak references is to implement caches or mappings holding large objects, where it’s desired that a large object not be kept alive solely because it appears in a cache or mapping.
我暂时没有看到太多适用的场景。
Tricks Python Plays with Immutables
CPython 对一些不可变类型做了特殊处理,以加速对它的使用。比如:
- 对于一个 tuple t,
t[:] is t
为真 - 对于 frozenset,
fs.copy() is fs
为真 s1 = 'a'
,s2 = 'a'
,s1 is s2
为真
这种机制叫做 interning。Wikipedia 上有一个 String interning 条目。