Why doesn't the metaclass function run for subclasses?

Python docs say that a class metaclass can be any callable . All the examples that I see use the class. Why not use the function? It can be called and quite easy to determine. But it does not work, and I do not understand why.

Here is my code:

class Foo(object): def __metaclass__(name, base, dict): print('inside __metaclass__(%r, ...)' % name) return type(name, base, dict) print(Foo.__metaclass__) class Bar(Foo): pass print(Bar.__metaclass__) 

Here's the conclusion:

 inside __metaclass__('Foo', ...) <unbound method Foo.__metaclass__> <unbound method Bar.__metaclass__> 

The metaclass method is defined for both the parent and child classes. Why is this caused only to the parent? (Yes, I tried using classmethod and staticmethod decorators for my metaclass, it doesn’t work. Yes, it may seem like a duplicate of a metaclass that is not called in subclasses , but they are a class, not a function, like a metaclass.)

+5
source share
1 answer

The answer is in the priority rules for __metaclass__ search :

The corresponding metaclass is determined by the following priority rules:

  • If dict['__metaclass__'] , it is used.
  • Otherwise , if there is at least one base class, its metaclass is used (first it searches for the __class__ attribute, and if it is not found, it uses its type).
  • Otherwise, if there is a global variable named __metaclass__ , it is used.
  • Otherwise, it uses a static style, a classic metaclass ( types.ClassType ).

If we look at Foo.__class__ , we find that it is <type 'type'> , which is expected as your metaclass function, called type , to build Foo .

__class__ set by type first parameter of type.__new__ , so in the classes of the class we call type.__new__(cls, name, bases, dict) (or super(Metaclass, cls).__new__(cls, ...) ). However, we cannot do this if the metaclass is a function:

 >>> def __metaclass__(name, base, dict): >>> print('inside __metaclass__(%r, %r, %r)' % (name, base, dict)) >>> return type.__new__(__metaclass__, name, base, dict) >>> class Foo(object): >>> __metaclass__ = __metaclass__ TypeError: Error when calling the metaclass bases type.__new__(X): X is not a type object (function) 

Similarly, if we try to install Foo.__class__ on your __metaclass__ , this will not work, since the __class__ attribute must be a class:

 >>> Foo.__class__ = Foo.__metaclass__.__func__ TypeError: __class__ must be set to new-style class, not 'function' object 

So, the reason that metaclass classes that inherit from type , as opposed to just called classes, is to make them inheritable.

+4
source

Source: https://habr.com/ru/post/1200749/


All Articles