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: using a metaclass by implementing the method __new__
or __init__
, with method __init_subclass__
in the body of a base class, or using class decorators.
An approach via decorators requires that they 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, both will have to have
__init_subclass__
- you would have to do a mixin for the base classes - then it can complicate more.
The mechanism that checks abstract ABC methods uses metaclasses - and it may be more natural to inherit the ABC metaclass and expand the checks made.
In this case, the check is also not so trivial - if you want to use the default Python bookmark @staticmethod
you will have to check in the base class if the method exists there - and it 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 both decorators as in your own example (abstractmethod as the innermost decorator)
Another detail is that the ABC of Python only of abstract method error is not redefined when the class is instantiated, not when it is declared - for static and class methods, this verification does not work, since they can be used without existing an instance of the class.
So, the business is actually writing a metaclass extending abc.ABCMeta
to do the checks on __init__
. This check has to include check all superclasses to see if there is any __abstractmethod__
, check whether it was overrided in the final class, and check if it was a staticmethod
: in Python, static methods are function indistinguishable, 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 bit more complex than that - why 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 must be able to get into the dictionaryo of class" B "when you are checking for static methods in class C. This is possible with the use of the __qualname__
attribute in methods, but it is not so trivial.
As you can see, there are many corner cases - it's really worth thinking if you need this mechanism as it is. It can be done, but it is a component of a complex framework, and it goes against the philosophy of language, where you must let things be stated, document what you need, and give a runtime error if something does not was well done. Of course, it is understandable that there are times when this is not desirable - so much so that typing and typing typing mechanisms have evolved in recent years.
I can write this metaclass, since I leave it here and in the GitHub gist and it is available for the future - just confirm that you really need it, please.