1

我在__metaclass__类的属性和实际继承之间有很多混淆,以及__new__在这两种情况下如何调用。我的问题来自于挖掘 django 框架中的一些模型代码。

假设我想将一个属性附加到一个类,因为它是在孩子的Meta子类中定义的:

class Parent(type):

    def __new__(cls, name, base, attrs):
        meta = attrs.pop('Meta', None)
        new_class = super(Parent, cls).__new__(cls, name, base, attrs)
        new_class.fun = getattr(meta, 'funtime', None)
        return new_class

我不明白为什么__new__在 django 的代码中调用实际方法,但是当我尝试编写类似的代码时它不起作用。

根据我的经验,以下实际上并没有调用__new__父级的方法:

class Child(Parent):
    class Meta:
       funtime = 'yaaay'

C = Child()

当我尝试这样做时,它会抱怨 TypeError:

TypeError: __new__() takes exactly 4 arguments (1 given)

但是,我一直在查看的源代码似乎以这种方式工作。

我知道可以使用元类来完成:

class Child(object):
    __metaclass__ = Parent

但我不明白为什么他们的方式适用于他们而不适用于我,因为非__metaclass___用于制作可分发模块会更清洁。

有人可以指出我所缺少的正确方向吗?

谢谢!

4

2 回答 2

1

在扩展的元类中type__new__用于创建一个类。

在一个类中,__new__用于创建一个实例。

元类是创建类的类。你对类继承和元类感到困惑。

你的Child类继承Parent,你想创建一个Child. 但是,Parent作为元类意味着Parent.__new__不应该用于创建类的实例。

于 2013-03-14T23:44:06.690 回答
1

在 django 中,Model不是元类。实际上元类是ModelBase. 这就是为什么他们的方式行得通而你的方式行不通的原因。
此外,最新的 django 使用辅助函数six.with_metaclass, 来包装“ModelBase”。

如果我们想遵循 django 的风格,ParentclassChild看起来像

def with_metaclass(meta, base=object):
    """Create a base class with a metaclass."""
    return meta("NewBase", (base,), {})

class ParentBase(type):
    def __new__(cls, name, base, attrs):
        meta = attrs.pop('Meta', None)
        new_class = super(ParentBase, cls).__new__(cls, name, base, attrs)
        new_class.fun = getattr(meta, 'funtime', None)
        return new_class

class Parent(with_metaclass(ParentBase)):
    pass

class Child(Parent):
    class Meta:
       funtime = 'yaaay'

c = Child()

>>> c.fun
'yaaay'

让我们专注于Parent。它几乎等同于

NewBase = ParentBase("NewBase", (object,), {})
class Parent(NewBase):
    pass
    

关键是怎么理解ParentBase("NewBase", (object,), {})
让我们回忆一下type()

类型(名称、基数、字典)

使用三个参数,返回一个新的类型对象。这本质上是类语句的动态形式。name字符串是类名,成为name属性;基元组逐项列出基类并成为属性;并且 dict 字典是包含类主体定义的命名空间,并成为dict属性。例如,以下两个语句创建相同的类型对象:

由于ParentBase是元类,是 的子类type。因此,ParentBase("NewBase", (object,), {})与 非常相似type("NewBase", (object,), {})。在这种情况下,唯一的区别是动态创建的类不是 的实例type,而是ParentBase。换句话说,元类NewBaseParentBaseParent相当于

class NewBase(object):
    __metaclass__ = ParentBase

class Parent(NewBase):
    pass
    

最后,我们得到了一个__metaclass__.

于 2013-03-15T00:02:01.650 回答