簡體   English   中英

python中的type和type.__new__有什么區別?

[英]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__()主要用於允許不可變類型的子類(如intstrtuple )自定義實例創建。 它也通常在自定義元類中被覆蓋,以自定義類創建。

所以在 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM