python中类型和类型.__ new__有什么区别?

时间:2010-04-09 15:32:43

标签: python types new-operator metaclass

我正在写一个元类,不小心这样做了:

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)

这两个元类之间究竟有什么区别?更具体地说,导致第一个不能正常工作的原因(某些类没有被元类调用)?

6 个答案:

答案 0 :(得分:7)

在第一个示例中,您将创建一个全新的类:

>>> 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__
>>> 

答案 1 :(得分:7)

您需要弄清楚的第一件事是object.__new__()如何运作。

来自documentation

  

object.__new__(cls[, ...])

     

调用以创建类cls的新实例。 __new__()是静态的   需要的方法(特殊的,所以你不需要声明它)   请求实例作为其第一个参数的类。   其余参数是传递给对象构造函数的参数   表达式(对类的调用)。 __new__()的返回值   应该是新的对象实例(通常是cls的实例)。

     

典型实现通过调用创建类的新实例   超类的__new__()方法使用super(currentclass, cls).__new__(cls[, ...])和适当的参数然后修改   在返回之前必要时新创建的实例。

     

如果__new__()返回cls的实例,则会调用新实例的__init__()方法__init__(self[, ...]),其中self是新实例和其余参数是相同的   传递给__new__()

     

如果__new__()未返回cls的实例,则不会调用新实例的__init__()方法。

     

__new__()主要用于允许不可变类型的子类(如intstrtuple)来自定义实例创建。也是   通常在自定义元类中重写以自定义类   创建

因此,在mg。answer中,前者不会调用函数__init__,而后者在调用__init__后调用函数__new__

答案 2 :(得分:4)

请参阅下面的注释,希望这有用。

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)

答案 3 :(得分:3)

return type(name, bases, dict)

您从中获得的是新的type,而不是MetaCls实例。因此,无法调用MetaCls(包括__init__)中定义的方法。

type.__new__将作为创建新类型的一部分进行调用,是的,但进入该函数的cls的值将是type而不是MetaCls

答案 4 :(得分:3)

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将在__call__的类型中查找MyMeta(在我们的案例中为type

  

“对于新式类,特殊方法的隐式调用是   只保证在对象的类型上定义时才能正常工作,而不是   在对象的实例字典“

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)将为MyMeta

设置正确的元类(MyClass

type(cls, bases, attributes)将为MyClass设置默认元类(类型)

答案 5 :(得分:1)

所有这些都很好描述here

如果没有返回正确类型的对象,则无需定义自定义元类。