![](/img/trans.png)
[英]Must type.__new__ and type.__init__ be passed the same parameters?
[英]What does class `type.__init__` and `type.__new__` do when creating a new `type` instance(i.e. class)?
眾所周知,在 Python 中創建新class
時,有很多工作是在幕后完成的,例如設置屬性,如__dict__
、 __class__
、 __metaclass__
等。
我知道在創建新類時,會調用type.__new__
方法,並且type.__init__
也會在call.__new__
返回type
的實例的情況下被調用。 所以我想這兩種方法可能負責一些工作,但我在文檔中找不到任何關於它們真正功能的描述。 我的問題是,這兩種方法究竟對創建class
有什么作用?
編輯:
我知道元類是什么,以及元類在創建type
實例的過程中大致做了什么,但我想知道這兩種方法如何協同完成創建type
實例的工作。 也許@BrenBarn 是正確的,這是與實現相關的。 我只是想確定一下。 例如,如果我在自己的元類T
中重寫__new__
方法,並直接返回type(clsname, bases, dct)
而不是像人們通常所做的那樣在基類type
中調用__new__
,那么T.__init__
和type.__init__
都不會被調用,因為返回的對象不是T
的實例。 如果是這樣,由於缺少__init__
,我會錯過什么? 而且,我是否可以期望這是跨各種實現的一致行為?
我想知道這兩種方法如何協作來完成創建類型實例的工作
type.__init__
方法只負責檢查是否有 1 個或 3 個參數以及是否沒有關鍵字參數。 C 源代碼在Objects/typeobject.c
的type_init()
函數中。
type.__new__
方法完成了創建新類的所有其余工作。 以下是Objects/typeobject.c
中type_new_impl
的步驟:
這是相當重要的,你是否在Python中實現或C.例如調用這些呼叫type(clsname, bases, dct)
是組合tp_new
和tp_init
,但會調用__init__
當且僅當它設置為tp_init
新創建類型的元類上的指針,如果該指針僅在其__dict__
具有__init__
,則不可以。 因此,您可以通過C而不是Python進行訪問。 當然,您可以自己顯式調用__init__
。
對於大多數類型的插槽,其中的__init__
,在type_new
也存在類似的問題:出於性能原因,即使它們是查找中的字符串,但選擇type_new
指向字符串的指針上,因此,僅當您使用插入的字符串時,對__init__
的調用才有效在您的tp_new
實施中。 是的,這種行為在Python版本之間,甚至在2.7版本,甚至在補丁版本之間,都發生了微妙的變化。
我非常確定,這些細微的問題是為什么您無法在文檔中找到太多有關這些問題的原因:如果記錄了該行為,則必須對其進行支持,而對於存在這種情況的常見情況,這將以性能為代價沒有用戶提供的tp_new
。
至於在新創建的類型上調用__init__
的原因:僅當您有額外的數據成員要初始化而不能作為傳遞給構造函數的字典的一部分時, type_new
(正常的type_new
會處理這些問題。)在C中定義新類型時經常會遇到這種情況,因為該類型可能承載內部數據,這些內部數據本來不應該是python可見的,但是用Python處理時並不常見。
作為一個極端的例子,考慮cppyy( http://cppyy.readthedocs.io/ )中的Python-C ++交叉繼承。 所有C ++代理類都有一個關聯的元類。 當從C ++類派生Python類並覆蓋C ++虛擬方法時,元類將被調用,並插入Python端不可見的調度程序類:它是C ++層次結構的一部分,但不是Python層次結構的一部分。 在正常的type_new運行之后執行此額外的設置。 (作為實現的詳細信息,這仍然全部在tp_new方法中完成,以避免出現何時調用tp_init以及何時不調用tp_init的問題。)請注意,此行為在創建派生類時需要一個鈎子,如果沒有該類,則無法完成元類。
但是,一般而言,Python端代碼在各個實現中的行為應相同,並且不會顯示C端的細微問題。 但潛在的差異可能很大,而不僅僅是上面提到的細微差別。 例如,PyPy根本不支持RPython中的元類,因此在這種情況下,您必須嵌入Python代碼。
就個人而言,在Python方面,我對__new__
需求要少__new__
。 實際上,這有點用詞不當:如果您只想控制類的創建,僅此而已,那么在假裝元類上實現__call__
可以提供相同的最終用戶語法,而麻煩卻少得多。 使用__new__
的唯一原因是,如果要通過其自定義元類方法/屬性控制其創建后的行為。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.