Metaclass not being called in subclasses

2019-01-18 23:48发布

问题:

Here is a python session.

>>> class Z(type):
    def __new__(cls, name, bases, attrs):
        print cls
        print name
        return type(name, bases, attrs)
...     
>>> class Y(object):
    __metaclass__ = Z
...     
<class '__main__.Z'>
Y
>>> class X(Y):
...     pass
... 
>>> class W(Y):
...     __metaclass__ = Z
...     
<class '__main__.Z'>
W
>>> 

After I define class X I expect Z._new__ to be called for it, and to print the two line, which is not happening, (as metaclass are inherited?)

回答1:

The problem is that the cls argument (which is the metaclass object) is not passed on when you call type, therefore the class object Y that is created and returned does not have any reference to the metaclass Z.

If you replace the last line in __new__ with

return super(Z, cls).__new__(cls, name, bases, attrs)

then it works. Note that even though cls is used in super we still have to provide cls as an argument as well, since super here returns an unbound method (see here for more).

As an alternative to using super one could use:

 return type.__new__(cls, name, bases, attrs)

The important thing is that we give cls (our metaclass object Z) to the classmethod __new__. The shorter form type(name, bases, attrs) fills in type itself for the cls argument, which is of course wrong. This error is similar to calling an instance method with the wrong self argument.

I prefer using super, since this is better style.