20

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

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)

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

4

6 回答 6

12

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

>>> 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__
>>> 
于 2010-04-09T16:34:27.193 回答
8

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

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)
于 2014-10-11T04:23:03.457 回答
8

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

这是来自以下文档

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

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

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

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

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

__new__()主要是为了允许不可变类型的子类(如intstrtuple)自定义实例创建。它也通常在自定义元类中被覆盖,以自定义类创建。

所以在mg.的回答中,前者不调用函数__init__,而后者调用__init__后调用函数__new__

于 2016-06-04T04:44:43.100 回答
5
return type(name, bases, dict)

你从中得到的是一个新的type,而不是一个MetaCls实例。因此,您在MetaCls(包括__init__)中定义的方法永远不能被调用。

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

于 2010-04-09T16:26:28.713 回答
5
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's 类型中查找(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

于 2012-11-01T18:12:41.643 回答
2

这一切都在这里描述得很好。

如果您没有返回正确类型的对象,那么定义自定义元类是没有意义的。

于 2010-04-09T15:49:21.700 回答