Search code examples
pythoninheritanceabstract-classpython-decoratorsabc

Why does @abstractmethod need to be used in a class whose metaclass is derived from ABCMeta?


PEP 3119 states that:

The @abstractmethod decorator should only be used inside a class body, and only for classes whose metaclass is (derived from) ABCMeta. Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are not supported.

I cannot find, however, an explanation of why that is. Specifically, I do not notice a difference in behavior when using only @abstractmethod in a class that does not explicitly inherit from ABCMeta. In the following simple example, if I understand correctly, the proper way of doing things would be:

import six
from abc import ABCMeta
from abc import abstractmethod

class Base(six.with_metaclass(ABCMeta)):
    def __init__(self):
        print('Init abstract base')

    @abstractmethod
    def do_something(self):
        pass

class Subclass(Base):
    def __init__(self):
        super(Subclass, self).__init__()

    def do_something(self):
        print('Done.')

sub = Subclass()
sub.do_something()

However, if I let the Base class inherit simply from object, and only use the decorator when needed, I notice no change in behavior.

from abc import abstractmethod

class Base(object):
    def __init__(self):
        print('Init abstract base')

    @abstractmethod
    def do_something(self):
        pass

class Subclass(Base):
    def __init__(self):
        super(Subclass, self).__init__()

    def do_something(self):
        print('Done.')

sub = Subclass()
sub.do_something()

I have found this to be the case even on more complex architectures, so I wonder: when does the latter method fail?


Solution

  • You don't see any difference because your first subclass does implement the do_something abstractmethod.

    Comment out the definition of do_something in the subclasses in both versions and you'll find out that in the first case you get a TypeError when trying to instanciate the subclass - you'd also get one trying to instanciate the first version Base class itself FWIW. With the second version, you can instanciate both classes (which shouldn't be possible since they are abstract) and call the abstract do_something method - which kind of defeats one of main points of ABCs.

    You'll also miss quite a few other interesting features of ABCs FWIW...