1

这里的代码胜于文字:

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


class A(object):
    __metaclass__ = MetaA

这将打印MetaA

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


B = type('B', (A, ), {'__metaclass__': MetaB})

这将再次打印MetaA(?!)

我希望:

MetaB
MataA

问题是:

  1. 为什么我MetaA只得到?
  2. 如何更改代码以获得:

    MetaB
    MataA
    
4

1 回答 1

2

原因是这type(name, bases, dict)不是创建具有指定元类的类的正确方法。

解释器实际上避免type(name, bases, dict)在看到__metaclass__定义的属性并调用 mateclass时调用,而不是 type(name, bases, dict)像通常那样调用。

这是解释它的相关文档部分:

读取类定义时,如果定义了 __ 元类 __,则分配给它的可调用对象将被调用,而不是 type()。这允许编写监视或更改类创建过程的类或函数 [...]

如果您像这样修改代码:

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

class A(object):
    __metaclass__ = MetaA


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

class B(A):
    __metaclass__ = MetaB

...然后你会得到预期的输出:

MetaA
MetaB 
MetaA 

(创建A类时打印第一行,创建B类时打印第二行和第三行)

更新:这个问题假设类的动态创建B,所以我会扩展我的答案。

B使用 metacalss 动态构造类,您应该做与解释器相同的事情,即在__metaclass__instad 中使用 metaclass 构造类type(name, bases, dict)

像这样:

B = MetaB('B', (A, ), {'__metaclass__': MetaB})
于 2013-05-22T08:15:27.473 回答