Force the implementation of certain type of abstract method

0

In Python it is possible to define classes abstratas and métodos abstratos in a slightly simple way, for example:

class Spam(ABC):

    @abstractmethod
    def grok(self):
        pass

This class can not be instantiated because it has the method grok is explicitly defined as abstract, this class must be extended and then overwritten this method, here is an example of my doubt:

class Spam(ABC):

    @staticmethod
    @abstractmethod
    def grok():
        pass


class Derived(Spam):

    def grok(self):
        pass

As I said before this class should be extended and then overwritten by the method grok , but see that this time the method, besides being abstract, defines that it is also static, but as you can see nothing compels me to have to define the same as static, only that I must define it. My question is: Como posso obrigar a implementação do método com seu tipo previamente definido na classe base abstrata?

    
asked by anonymous 22.10.2018 / 03:36

1 answer

3

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.

    
22.10.2018 / 16:07