Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if method is still abstract

I have one base class with abstractmethod and subclass which implements this method. How to determine in runtime if in object (without checking type of object or calling method) that method is abstract or not?

class Base:
    @abc.abstractmethod
    def someAbstractMethod(self):
        raise NotImplementedError("Not implemented yet.")


class Subclass(Base):
    def someAbstractMethod(self):
        some_operations

objects = [Base(),Subclass(),Base(),Subclass(),Subclass(),...]
for object in objects:
    #here I want check if method is still abstract or not
like image 293
Piotr Wasilewicz Avatar asked Dec 03 '22 12:12

Piotr Wasilewicz


1 Answers

Python prevents the creation of instances for classes with abstract methods. So just the fact that you have an instance means you have no abstract methods.

You do, however, have to use the ABCMeta metaclass to properly trigger this behaviour:

class Base(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def someAbstractMethod(self):
        raise NotImplementedError("Not implemented yet.")

You can also inherit from abc.ABC to get the same metaclass via a base class.

If you wanted to see what abstract methods a class might have listed, use the __abstractmethods__ attribute; it is a set of all names that are still abstract:

>>> import abc
>>> class Base(metaclass=abc.ABCMeta):
...     @abc.abstractmethod
...     def someAbstractMethod(self):
...         raise NotImplementedError("Not implemented yet.")
...
>>> class Subclass(Base):
...     def someAbstractMethod(self):
...         some_operations
...
>>> Base.__abstractmethods__
frozenset({'someAbstractMethod'})
>>> Subclass.__abstractmethods__
frozenset()

All that the @abc.abstractmethod decorator does is set a __isabstractmethod__ attribute on the function object; it is the metaclass that then uses those attributes.

So if you are dig in too deep or are using a third-party library that has forgotten to use the ABCMeta metaclass, you can test for those attributes:

>>> class Base:    # note, no metaclass!
...     @abc.abstractmethod
...     def someAbstractMethod(self):
...         raise NotImplementedError("Not implemented yet.")
...
>>> getattr(Base().someAbstractMethod, '__isabstractmethod__', False)
True

If you need to 'repair' such broken abstract base classes, you'd need subclass and mix in ABCMeta, and add a __abstractmethods__ frozenset to the class you are inheriting from:

BaseClass.__abstractmethods__ = frozenset(
    name for name, attr in vars(BaseClass).items()
    if getattr(attr, '__isabstractmethod__', False))

class DerivedClass(BaseClass, metaclass=abc.ABCMeta):
    # ...

Now DerivedClass is a proper ABC-derived class where any abstract methods are properly tracked and acted on.

like image 129
Martijn Pieters Avatar answered Dec 27 '22 11:12

Martijn Pieters