變量不是盒子
1997 年夏天,我在 MIT 學了一門 Java 課程。Lynn Andrea Stein 教授
(一位獲獎的計算機科學教育工作者,目前在歐林工程學院教書)指
出,人們經常使用“變量是盒子”這樣的比喻,但是這有礙于理解面向對
象語言中的引用式變量。Python 變量類似于 Java 中的引用式變量,因此
最好把它們理解為附加在對象上的標注。
在示例 8-1 所示的交互式控制臺中,無法使用“變量是盒子”做解釋。圖
8-1 說明了在 Python 中為什么不能使用盒子比喻,而便利貼則指出了變
量的正確工作方式。
示例 8-1 變量 a 和 b 引用同一個列表,而不是那個列表的副本
>>> a = [1, 2, 3]
>>> b = a
>>> a.append(4)
>>> b
[1, 2, 3, 4]
圖 8-1:如果把變量想象為盒子,那么無法解釋 Python 中的賦值;
應該把變量視作便利貼,這樣示例 8-1 中的行為就好解釋了
Stein 教授還反復講解了賦值方式。例如講到 seesaw 對象時,她會說“把
變量 s 分配給 seesaw”,絕不會說“把 seesaw 分配給變量 s”。對引用式
變量來說,說把變量分配給對象更合理,反過來說就有問題。畢竟,對
象在賦值之前就創建了。示例 8-2 證明賦值語句的右邊先執行。
示例 8-2 創建對象之后才會把變量分配給對象
>>> class Gizmo:
... def __init__(self):
... print('Gizmo id: %d' % id(self))
...
>>> x = Gizmo()
Gizmo id: 4301489152 ?
>>> y = Gizmo() * 10 ?
Gizmo id: 4301489432 ?
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for *: 'Gizmo' and 'int'
>>>
>>> dir() ?
['Gizmo', '__builtins__', '__doc__', '__loader__', '__name__',
'__package__', '__spec__', 'x']
? 輸出的 Gizmo id: … 是創建 Gizmo 實例的副作用。
? 在乘法運算中使用 Gizmo 實例會拋出異常。
? 這里表明,在嘗試求積之前其實會創建一個新的 Gizmo 實例。
? 但是,肯定不會創建變量 y,因為在對賦值語句的右邊進行求值時拋
出了異常。