Checks and transformations on the methods and attributes of a class at the time they are created can be done in three different ways in Python: with the use of a metaclass implementing the method __new__
or __init__
, with the method __init_subclass__
in the body of a base class, or using class decorators.
An approach via decorators require them to be used in each class of their hierarchy - the decorator is not "inherited". __init_subclass__
may be legal in some cases, but will mix code in your Base class. If there is more than one base class in your project, you will both have to have the __init_subclass__
- you would have to do a mixin for the base classes - so it might complicate more.
The mechanism that checks abstract methods of ABC uses metaclasses - and it may be more natural to inherit the metaclass of ABC and expand the checks made.
In this case, checking is also not so trivial - if you want to use the default Python marker @staticmethod
will have to check in the base class if the method exists there - and exists as Static, and then raise an exception. (In the documentation there is even a @abstractstaticmethod
but it is obsolete - the recommendation is to use the two decorators as in his same example (abstractmethod as the innermost decorator)
Another detail is that the Python ABC only of abstract method error not reset when the class is instantiated, not when it is declared - for static and class methods, this check does not help, since it can be used without an instance of the class.
So the thing is to actually write a metaclass extending abc.ABCMeta
to carry out the checks on __init__
. This check has to include checking all superclasses to see if there are any __abstractmethod__
, check whether or not he was overrided in the final class, and check whether he was a staticmethod
: in Python, static methods are indistinguishable from function, so you have to retrieve the method from the dictionary of the class where it is set and check with isinstance(cls.__dict__["method_name"], staticmethod)
.
But it’s a little more complex than that - because if you have a base class with the abstract methods "A", a subclass "B" that implements the abstract methods, and another subclass C that inherits from "B", you have to get to the dictionary of the class "B" when checking the static methods in class C. This is possible with the use of the attribute __qualname__
in the methods, but it is not so trivial.
As you can see, there are many corner cases - it’s really worth wondering if you need this mechanism as it is. It can be done, but it’s a component of a complex framework, and it goes against the philosophy of language, where you have to let things be stated, document what you need, and make an execution-time mistake if something hasn’t been done right. Of course it is understandable that there are times when this is not desirable - so much so that in recent years the mechanisms of indication of typing with Typing and mypy have evolved a lot.
I can write this metaclass, since I leave it here and in the gist of Github and it is available for the future - just confirm that you really need it, please.
And does it make sense for a static method to be abstract? Because it is static it will belong only to the class, so why inherit it it abstractly? What’s the point in doing this?
– Woss
I saw a video on the Internet that guy created a basic abstraction of a
Pizza
and had a static method calledingredientes
, that returned the ingredients of the same. This method should be overwritten in the base classes so he made itabstrato
, but as I said nothing requires that the same be implemented beingestático
.– ThiagoO
https://www.giacomodebidda.com/factory-method-and-abstract-factory-in-python/
– ThiagoO