FluentPythonCh08

 25th December 2017 at 7:08pm

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])

这跟 Java 的 call by reference 机制是类似的(但是 Java 对于原始类型,使用的是 call by value)。

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 可以解除一个变量与其关联的对象的绑定,这样会使该对象的引用计数减少。

不要依赖于 CPython 的垃圾回收机制。不同的 Python 实现,使用的 GC 策略是不一样的。CPython 使用了引用计数,但是 Jython / IronPython 分别使用了他们宿主的 GC 机制,所以行为上跟 CPython 不一致。比如 Jython 实现上,一个 object 无法被访问到时,它也不会马上被回收,而在 CPython 中它会马上被回收。

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 对一些不可变类型做了特殊处理,以加速对它的使用。比如:

  1. 对于一个 tuple t,t[:] is t 为真
  2. 对于 frozenset,fs.copy() is fs 为真
  3. s1 = 'a', s2 = 'a', s1 is s2 为真

这种机制叫做 interning。Wikipedia 上有一个 String interning 条目。