python中type和type.__new__有什么区别?

22 投票
6 回答
12156 浏览
提问于 2025-04-15 21:25

我在写一个元类的时候,不小心写成了这样:

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 个回答

9

首先,你需要了解一下 object.__new__() 是怎么工作的。

下面是来自官方文档的解释:

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. 的回答中,前者没有调用 __init__ 函数,而后者在调用 __new__ 后调用了 __init__ 函数。

9

请参考下面的注释,希望对你有帮助。

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)
16

在第一个例子中,你是在创建一个全新的类:

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

撰写回答