在Python的世界裏,幾乎全部的東西都是可變的。html
類方法 __init_subclass__
從 3.6
(whatsnew3.6) 引入,做用是能夠在不使用元類的狀況下改變子類的行爲。也就是說它是獨立於元類編程的,也能達到編輯其餘類的一種手段。那麼,如何來理解它呢?python
按照文檔中的示例:編程
class Philosopher:
def __init_subclass__(cls, default_name, **kwargs):
super().__init_subclass__(**kwargs)
cls.default_name = default_name
class AustralianPhilosopher(Philosopher, default_name="Bruce"):
pass
複製代碼
示例中,當有子類繼承了 Philosopher
,那麼 __init_subclass__
就會調用。內容也很簡單,父類 Philosopher
爲它的全部子類都設置了 default_name 屬性。bash
__init_subclass__
就像是個鉤子函數,當子類定義以後觸發。函數
默認實現 object.__init_subclass__
不執行任何操做。但默認實現不能傳遞任何參數,不然報錯。ui
值得注意的是,示例中的 __init_subclass__
第一個參數是 cls
而不是常見的 self
。這是由於這個方法隱式地被 @classmethod
裝飾(PEP-487)。這個決定實際上是因爲多數人忘記給 __prepare__
加上 @classmethod
裝飾了,帶來了很差的用戶體驗;並且在另外一個PEP中 __prepare__
被記錄爲普通的方法,因此已經很差改它了。而做爲 3.6 新方法的 __init_subclass__
所以就有理由隱式裝飾了(其實我比較喜歡顯式,由於 Python 之禪中提到的 "顯示優於隱式")。spa
儘管 __init_subclass__
是獨立於元類編程的,但類都是由默認元類 type
建立的,那麼在 type.__new__()
建立了類以後會有哪些步驟:code
type.__new__
收集類命名空間定義的 set_name() 方法的全部描述符;__set_name__
的特定描述符在特定的狀況下調用;__init_subclass__()
。若類被裝飾器裝飾,那麼就將上述生成的對象傳遞給類裝飾器。htm
總的來講,__init_subclass__()
是鉤子函數,它解決了如何讓父類知道被繼承的問題。鉤子中能改變類的行爲,而沒必要求助與元類或類裝飾器。鉤子用起來也更簡單且容易理解。對象
雖然本文還提到了 __set_name__
,但它和 __init_subclass__
並不相互關聯, __set_name__
主要是解決了如何讓描述符知道其屬性的名稱。
__init_subclass__
的目標是提供更簡單的定製方式,在簡單的場景下是元類的替代品。值得試一試。