Fluent Python 讀書筆記(八)
類別中繼編程
- 「中繼類別是比 99% 使用者鎖想的還要艱深的魔法。如果你想知道自己是否須要他們,其實你不需要。」
- 類別中繼邊程是在執行階段建立/自訂類別的技巧
- 類別是一級物件,無論什麼時候,都可以用函式來建立新的類別(諸如類別修飾器),不需要
class
關鍵字 - 中繼類別很強大,但很難正確使用,事實上,很難在實際的程式中使用
- 編寫中繼程式的先決條件是了解匯入階段與執行階段的差異
- 「如果你不在製作框架,就不該編寫中繼類別」
自訂一個類似 collections.namedtuple
的簡易紀錄型類別工廠
對這種紀錄型的類別而言,我們希望屬性群都是相同的,而且擁有相同順序
此範例建立的類別有一個限制:無法被序列化,即無法與 picle 模組的 dump
、load
函式一起使用
在類別中定義 __slots__
,等於告訴解譯器「它們都是這個類別的實例屬性」,這些屬性會被存在一個類 tuple 結構,避免每一個實例的 __dict__
產生記憶體開銷
當類別指定 __slots__
時,它的實例無法使用任何指定之外的屬性(這是一種副作用/缺點),單純為了做這種屬性限制而使用 __slots__
並不是一個好的作法。__slots__
的目標是最佳化,而不是限制開發者的行為
- 我們通常會把
type
當成函式來用,但它同時是個類別,如果你用三個引數來呼叫,它的行為就像類別,會實體化一個新的類別 - 避免使用
exec
或eval
來編寫中繼程式是一種好的習慣。如果不受信任的來源傳送字串/段落給這些函式,會造成嚴重的後果;Python 提供足夠的自我檢查工具,exec
、eval
在大部分情況下沒必要使用 - 類別描述器有一個重大的缺點在於,它們只能在直接套用的類別上動作——被修飾類別的子類別可能不會繼承修飾器所作的改變
- 「匯入階段(import time)」、「執行階段(runtime)」這些用語沒有經過嚴謹定義,而且它們之間有灰色地帶
- 在匯入階段,解譯器會完整解析
.py
模組的程式碼,並產生執行的 bytecode——這就是可能會發生語法錯誤的地方(如果在本地的__pycache__
有最新的.pyc
,這個步驟會被跳過) - 雖然編譯(compiling)的確是匯入階段的動作,但這個階段也會發生其他事情——特別是
import
陳述式,它並非只是一個宣告(對比 Java 的 import)——當程序首次匯入模組時(尚無快取時),它會執行被匯入模組的所有最高層級的程式碼,包含執行階段的行為 - 最高層級的程式碼特指類別的內文(包含嵌套的類別),解譯器會在匯入階段執行它們;相反地,對函式而言,解譯器只會編譯內文、將函式物件加到全域名稱,但不會執行函式內文
- 類別若是有使用類別修飾器,該修飾器函式也會被執行
- 類別的中繼類別的方法
__init__
也會被執行