对象引用、可变性、垃圾回收

  • 变量不是盒子,变量是便利贴:把变量s分配给对象xxx
  • == 比较两个对象的值(存储的数据),is比较对象的标识(identity)
    • is== 快,因为它不能被重载,Python可以直接比较两个整数ID
    • 一般is都用于和None做比较,但==也能有同样的效果,只是速度慢一些
  • 浅拷贝复制最外层容器,覆盖的是与源容器中的引用,如果有可变项,则会有意想不到的问题,这个时候应该使用深拷贝
  • Python唯一支持的参数传递模式是共享传参(call by sharing),由此函数可能会修改作为参数传入的可变对象,而不改变其标识(it cannot altogether replace an object with another)
"""
>>> 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']

>>> dir(HauntedBus.__init__)  # doctest: +ELLIPSIS
['__annotations__', '__call__', ..., '__defaults__', ...]
>>> HauntedBus.__init__.__defaults__
(['Carrie', 'Dave'],)
>>> HauntedBus.__init__.__defaults__[0] is bus2.passengers
True
"""

class HauntedBus:
    """A bus model haunted by ghost passengers"""

    def __init__(self, passengers=[]): 
        self.passengers = passengers

    def pick(self, name):
        self.passengers.append(name)  

    def drop(self, name):
        self.passengers.remove(name)

  • 创建对象时没有传入passengers,导致对象去引用类创建时产生的默认列表,变量成为参数默认值的别名
  • del和垃圾回收:Python垃圾回收使用的主要算法是引用计数,当其归零时立即被销毁
    • 可以通过weakref.finalize注册回调函数,在销毁对象时调用
      • 传入这个函数的引用是弱引用,不会增加对象的引用计数
        >>> import weakref
        >>> s1 = {1, 2, 3}
        >>> s2 = s1
        >>> def bye():
        ... print('...like tears in the rain.') 
        ...
        >>> ender = weakref.finalize(s1, bye)
        >>> ender.alive 
        True
        >>> del s1
        >>> ender.alive 
        True
        >>> s2 = 'spam'
        ...like tears in the rain. 
        >>> ender.alive 
        False
        
  • 变量作用域
    • Python does not require you to declare variables, but assumes that a variable assigned in the body of a function is local. This is much better than the behavior of JavaScript, which does not require variable declarations either, but if you do forget to declare that a variable is local (with var), you may clobber a global variable without knowing.
    • local, global, nonlocal
  • When a function is defined, the Python bytecode compiler determines how to fetch a variable x that appears in it, based on these rules:
    • If there is a global x declaration, x comes from and is assigned to the x global variable module
    • If there is a nonlocal x declaration, x comes from and is assigned to the x local variable of the nearest surrounding function where x is defined.
    • If x is a parameter or is assigned a value in the function body, then x is the local variable.
    • If x is referenced but is not assigned and is not a parameter:
      • x will be looked up in the local scopes of the surrounding function bodies (nonlocal scopes).
      • If not found in surrounding scopes, it will be read from the module global scope.
      • If not found in the global scope, it will be read from __builtins__.__dict__.
>>> def f1(a):
...     print(a)
...     print(b)
...
>>> f1(3)
3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in f1
NameError: name 'b' is not defined
>>> b = 6
>>> f1(3)
3
6
>>> def f2(a):
...     print(a)
...     print(b)
...     b = 9
...
>>> f2(3)
3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in f2
UnboundLocalError: local variable 'b' referenced before assignment
# tag::F1_DIS[]
>>> from dis import dis
>>> dis(f1)
  2           0 LOAD_GLOBAL              0 (print)  <1>
              3 LOAD_FAST                0 (a)  <2>
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              9 POP_TOP

  3          10 LOAD_GLOBAL              0 (print)
             13 LOAD_GLOBAL              1 (b)  <3>
             16 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             19 POP_TOP
             20 LOAD_CONST               0 (None)
             23 RETURN_VALUE
# end::F1_DIS[]
# tag::F2_DIS[]
>>> dis(f2)
  2           0 LOAD_GLOBAL              0 (print)
              3 LOAD_FAST                0 (a)
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              9 POP_TOP

  3          10 LOAD_GLOBAL              0 (print)
             13 LOAD_FAST                1 (b)  <1>
             16 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             19 POP_TOP

  4          20 LOAD_CONST               1 (9)
             23 STORE_FAST               1 (b)
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE
# end::F2_DIS[]