Python: why do I need super().__init__() call in metaclasses?

Viewed 429

I have got one question: why do I need to call super().--init--() in metaclasses? Because metaclass is factory of classes, I think we don`t need to call initialization for making objects of class Shop. Or with using super().--init-- we initializing the class? (Because my IDE says, that I should call it. But without super().--init-- nothing happens, my class working without mistakes). Can you explane me, why? Thanks in advance!

class Descriptor:
    _counter = 0

    def __init__(self):
        self.attr_name = f'Descriptor attr#{Descriptor._counter}'
        Descriptor._counter += 1

    def __get__(self, instance, owner):
        return self if instance is None else instance.__dict__[self.attr_name]

    def __set__(self, instance, value):
        if value > 0:
            instance.__dict__[self.attr_name] = value
        else:
            msg = 'Value must be > 0!'
            raise AttributeError(msg)

class Shop():
    weight = Descriptor()
    price = Descriptor()


    def __init__(self, name, price, weight):
        self.name = name
        self.price = price
        self.weight = weight
        
    def __repr__(self):
        return f'{self.name}: price - {self.price} weight - {self.weight}'

    def buy(self):
        return self.price * self.weight


class Meta(type):
    def __init__(cls, name, bases, attr_dict):
        super().__init__(name, bases, attr_dict) # <- this is that func. call
        for key, value in attr_dict.items():
            if isinstance(value, Descriptor): # Here I rename attributes name of descriptor`s object.
                value.attr_name = key

    @classmethod
    def __prepare__(metacls, name, bases):
        return OrderedDict()
1 Answers

You don't "need" to - and if your code use no other custom metaclasses, not calling the metaclass'__init__.super() will work just the same.

But if one needs to combine your metaclass with another, through inheritance, without the super() call, it won't work "out of the box": the super() call is the way to ensure all methods in the inheritance chain are called.

And if at first it looks like that a metaclass is extremely rare, and combining metaclasses would likely never take place: a few libraries or frameworks have their own metaclasses, including Python's "abc"s (abstract base classes), PyQT, ORM frameworks, and so on. If any metaclass under your control is well behaved with proper super() calls on the __new__, __init__ and __call__ methods, (if you override those), what you need to do to combine both superclasses and have a working metaclass can be done in a single line:

CompatibleMeta = type("CompatibleMeta", (meta, type(OtherClassBase)), {})

This way, for example, if you want to use the mechanisms in your metaclass in a class using the ABCMeta functionalities in Python, you just do it. The __init__ method in your Meta will call the other metaclass __init__. Otherwise it would not run, and some subtle unexpectd thing would not be initialized in your classes, and this could be a very hard to find bug.

On a side note: there is no need to declare __prepare__ in a metaclass if all it does is creating an OrderedDict on a Python newer than 3.6: Since that version, dicitionaries used as the "locals()" while executing class bodies are ordered by default. Also, if another metaclass you are combining with also have a __prepare__, there is no way to make that work automatically by using "super()" - you have to check the code and verify which of the two __prepare__s should be used, or create a new mapping type with features to attend both metaclasses.

Related