Fluent Python 讀書筆記(三)
物件參考、可變性與重複使用
- 「變數是標籤,不是盒子」
- 使用參考變數 (reference variable) 時,說「變數被指派給一個物件」會比較合理,畢竟——物件是在賦值之前建立的
- 兩個變數被指派到同一個物件時,這兩個變數互為「別名(alias)」
- 「每一個物件都有一個身份(ID)、一個型態跟一個值」,在 CPython,這個身份是
id()
,回傳物件的記憶體位置(不同解譯器可能會使用不同東西作為 ID) ==
比較物件的值;is
比較物件的 IDis
比==
快,因為它無法多載(不需要尋找或呼叫特殊方法來演算出一個值)- 原始物件的
__eq__
會比較 ID,但大多數覆寫__eq__
的情況通常會加入或使用別的比較 tuple
不可變的意思是「保存在它當中的物件參考 ID 不變」,即使tuple
可能存了可變的物件- 淺複製 (shallow copy) 即容器本身會被複製,但新的容器裡面保存的是舊的參考,例如
arr[:]
、arr.copy()
、copy(arr)
- 實作 deep copy 要小心物件可能會循環參考 (Ring),要判斷物件是否已經複製過
- 覆寫
__copy__
和__deepcopy__
可以控制copy.copy()
及copy.deepcopy()
的行為 - Python 函式傳遞的是參考(call by sharing) —— 即函數的參數 (parameter) 會指向引數 (argument) 的參考,換句話說,「函式內的參數就是其實際引數的別名」
- 同上,這也是為什麼「函式的預設參數不要使用可變型態」,簡單的改良:預設為 None,在函式中判斷是否初始化新的可變物件
del
刪除的是參考,而不是物件本身;物件只有在「參考數量變成零」的情況下才有可能被回收,這種銷毀可能不是立即性的- CPython 回收記憶體的演算法主要是計算參考數量,這個參考數量存在物件本身,但假若有循環參考時,容易發生 memory leak
- 在 CPython 的實作下,對
tuple
、str
、bytes
而言s[:]
不會製作複本,而是回傳物件的參考 - 在使用執行緒時,修改可變物件很難得到正確的結果:無法適當同步的執行序,會導致資料損毀;過度同步的執行序,會造成 deadlock
弱參考 (Weak Reference)
- 常用在使用快取的情境下,須要「參考一個不會被保存太久的物件」
- 弱參考是一種可呼叫的物件,它會回傳參考的物件,或者 None
- 使用弱參考而非賦值,就不會讓物件的「參考數量」增加
- 考慮使用
WeakKeyDictionary
、WeakValueDictionary
、WeakSet
與finalize
這些內部使用弱參考的高階界面,而非自己用weakref.ref
實作 - 因為實作的限制,
list
跟dict
的子類別可以被弱參考(原始型態不行),而int
、tuple
則完全無法被弱參考
1 2 3 4 5 6 7 8 9 10 11 12 13 |
>>> import weakref >>> a_set = {0, 1} >>> wref = weakref.ref(a_set) >>> wref() {0, 1} >>> a_set = {2,3,4} >>> wref() {0, 1} >>> wref() is None False >>> wref() is None True |
字串常值的共用,是一種優化技術,稱為 interning,Cpython 會對小型的整數使用相同的技術,來避免沒必要的重複
1 2 3 4 5 |
>>> s1 = 'ABC' >>> s2 = 'ABC' >>> s1 is s2 True |