【问题标题】:How is everything happened when a class's metaclass is a subclass of its subclass's metaclass?当一个类的元类是其子类元类的子类时,一切是如何发生的?
【发布时间】:2018-06-21 01:42:07
【问题描述】:

这是关于 Python2.7 中的元类的。

要清楚,代码是这样的:

class MetaC(type):
    def __new__(cls, name, bases, attrs):
        print "MetaC"
        return super(MetaC, cls).__new__(cls, name, bases, attrs)

class MetaB(MetaC):
    def __new__(cls, name, bases, attrs):
        print "MetaB"
        return super(MetaB, cls).__new__(cls, name, bases, attrs)

class MetaA(MetaB):
    def __new__(cls, name, bases, attrs):
        print "MetaA"
        return super(MetaA, cls).__new__(cls, name, bases, attrs)

class A(object):
    __metaclass__ = MetaA

print type(A)

class B(A):
    __metaclass__ = MetaB

print type(B)

class C(B):
    __metaclass__ = MetaC

print type(C)

结果如下:

MetaA
MetaB
MetaC
<class '__main__.MetaA'>
MetaB
MetaC
MetaA
MetaB
MetaC
<class '__main__.MetaA'>
MetaC
MetaA
MetaB
MetaC
<class '__main__.MetaA'>

似乎在创建类C时,MetaA.__new__是在MetaC.__new__之后调用的。

为了弄清楚,我查看了源代码并在typeobject.c中找到:

static PyObject *
type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
{
    /* some code is omitted here */
    for (i = 0; i < nbases; i++) {
        tmp = PyTuple_GET_ITEM(bases, i);
        tmptype = tmp->ob_type;
        if (tmptype == &PyClass_Type)
            continue; /* Special case classic classes */
        if (PyType_IsSubtype(winner, tmptype))
            continue;
        if (PyType_IsSubtype(tmptype, winner)) {
            winner = tmptype;
            continue;
        }
        PyErr_SetString(PyExc_TypeError,
                        "metaclass conflict: "
                        "the metaclass of a derived class "
                        "must be a (non-strict) subclass "
                        "of the metaclasses of all its bases");
        return NULL;
    }
    if (winner != metatype) {
        if (winner->tp_new != type_new) /* Pass it to the winner */
            return winner->tp_new(winner, args, kwds);
        metatype = winner;
    }
    /* some code is omitted here */
}

for 循环找到最深的子类winner(抱歉我的英语不好,代码可能比我说的要清楚得多)。如果不是metatype,将调用winner-&gt;tp_new。这就是我从代码中得到结果的原因。

那么,我想知道的是,为什么要这样设计?是否有任何务实的案例表明这样做的必要性?

另外,我也注意到了错误信息

元类冲突:派生类的元类必须是其所有基类的元类的(非严格)子类

显然,在我的例子中,C 的元类 MetaC 不是其基类的任何元类的子类。那么,它是一个非严格的子类吗?这里的“非严格”是什么意思?

【问题讨论】:

  • 你为什么仍然使用 Python 2 来研究 Python 的这些高级方面?最新的 Python 2 到现在已经 8 年了,2 年后 EOL。最新版本 (3.6) 可能会更好地提供您想通过元类在“现实世界”中活动的任何内容

标签: python inheritance metaclass cpython


【解决方案1】:

当然,你提出的具体案例是最虚构的。

发生的情况是,对于给定的类层次结构(从 A 继承的 B),必须同时存在 A 和 B 的元类的特征才能正确创建 B。一种合理确定 B 的元类的方法合并 A 的元类所做的任何事情,如果 B 的元类是 A 的元类的子类。

在更现实的场景中,您可以拥有一个专门用于元类的层次结构树,并且在树的下方,在多重继承场景中,人们会希望“G”类的元类包含在各种它继承的分支。

它实际上是唯一有意义的设计——尽管它是一个极端的案例,但它几乎永远无法实现。你的 rs 是一个完全人为的例子来强调这个极端情况,而且它刚刚成功。

改写为您的示例,当在那里实例化您的“C”类时,这意味着虽然您的“C”作为子类也是“B”和“A”,初始化“B”和“A”时需要运行的代码不会运行。

至于另一个问题“非严格子类”,它意味着派生的元类可以使用多重继承,并且如果它们也继承自父元类的元类,则它们可以是其父元类以外的其他元类的子类。

【讨论】:

    猜你喜欢
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 2010-10-21
    • 2011-05-31
    • 1970-01-01
    • 1970-01-01
    • 2014-05-25
    • 1970-01-01
    相关资源
    最近更新 更多