[英]What is the difference between type and type.__new__ in python?
我正在寫一個元類,不小心這樣做了:
class MetaCls(type):
def __new__(cls, name, bases, dict):
return type(name, bases, dict)
...而不是這樣:
class MetaCls(type):
def __new__(cls, name, bases, dict):
return type.__new__(cls, name, bases, dict)
這兩個元類之間究竟有什么區別? 更具體地說,是什么導致第一個無法正常工作(元類沒有調用某些類)?
在第一個示例中,您正在創建一個全新的類:
>>> class MetaA(type):
... def __new__(cls, name, bases, dct):
... print 'MetaA.__new__'
... return type(name, bases, dct)
... def __init__(cls, name, bases, dct):
... print 'MetaA.__init__'
...
>>> class A(object):
... __metaclass__ = MetaA
...
MetaA.__new__
>>>
而在第二種情況下,你打電話給父母的__new__
:
>>> class MetaA(type):
... def __new__(cls, name, bases, dct):
... print 'MetaA.__new__'
... return type.__new__(cls, name, bases, dct)
... def __init__(cls, name, bases, dct):
... print 'MetaA.__init__'
...
>>> class A(object):
... __metaclass__ = MetaA
...
MetaA.__new__
MetaA.__init__
>>>
您需要弄清楚的第一件事是object.__new__()
是如何工作的。
這是來自以下文檔:
object.__new__(cls[, ...])
調用以創建類
cls
的新實例。__new__()
是一個靜態方法(特殊情況,所以你不需要這樣聲明它),它將請求實例的類作為它的第一個參數。 其余參數是傳遞給對象構造函數表達式(對類的調用)的參數。__new__()
的返回值應該是新的對象實例(通常是cls
的實例)。典型的實現通過使用帶有適當參數的
super(currentclass, cls).__new__(cls[, ...])
調用超類的__new__()
方法來創建類的新實例,然后在返回之前根據需要修改新創建的實例它。如果
__new__()
返回cls
的實例,則新實例的__init__()
方法將像__init__(self[, ...])
一樣被調用,其中self
是新實例,其余參數與傳遞給的參數相同__new__()
。如果
__new__()
不返回cls
的實例,則不會調用新實例的__init__()
方法。
__new__()
主要用於允許不可變類型的子類(如int
、str
或tuple
)自定義實例創建。 它也通常在自定義元類中被覆蓋,以自定義類創建。
所以在 mg. 的回答中,前者不調用函數__init__
而后者在調用__new__
后調用函數__init__
。
請參考下面的注釋,希望對您有所幫助。
class MetaCls(type):
def __new__(cls, name, bases, dict):
# return a new type named "name",this type has nothing
# to do with MetaCls,and MetaCl.__init__ won't be invoked
return type(name, bases, dict)
class MetaCls(type):
def __new__(cls, name, bases, dict):
# return a new type named "name",the returned type
# is an instance of cls,and cls here is "MetaCls", so
# the next step can invoke MetaCls.__init__
return type.__new__(cls, name, bases, dict)
class MyMeta(type):
def __new__(meta, cls, bases, attributes):
print 'MyMeta.__new__'
return type.__new__(meta, cls, bases, attributes)
def __init__(clsobj, cls, bases, attributes):
print 'MyMeta.__init__'
class MyClass(object):
__metaclass__ = MyMeta
foo = 'bar'
實現相同結果的另一種方法:
cls = "MyClass"
bases = ()
attributes = {'foo': 'bar'}
MyClass = MyMeta(cls, bases, attributes)
MyMeta
是可調用的,因此 Python 將使用特殊方法__call__
。
Python 將在MyMeta
的類型(在我們的例子中是type
中查找__call__
“對於新式類,特殊方法的隱式調用只有在對象類型上定義時才能保證正常工作,而不是在對象的實例字典中”
MyClass = MyMeta(...)
被解釋為:
my_meta_type = type(MyMeta)
MyClass = my_meta_type.__call__(MyMeta, cls, bases, attributes)
在type.__call__()
里面,我想象的是這樣的:
MyClass = MyMeta.__new__(MyMeta, cls, bases, attributes)
meta_class = MyClass.__metaclass__
meta_class.__init__(MyClass, cls, bases, attributes)
return MyClass
MyMeta.__new__()
將決定MyClass
的構建方式:
type.__new__(meta, cls, bases, attributes)
將為MyClass
設置正確的元類(即MyMeta
)
type(cls, bases, attributes)
將為MyClass
設置默認元類(即類型)
return type(name, bases, dict)
你從中得到的是一個新的type
,而不是一個MetaCls
實例。 因此,您在MetaCls
定義的方法(包括__init__
)永遠無法被調用。
type.__new__
將作為創建新類型的一部分被調用,是的,但是進入該函數的cls
的值將是type
而不是MetaCls
。
這一切都描述得很好here 。
如果您沒有返回正確類型的對象,則定義自定義元類毫無意義。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.