6

I found multiple (slightly different) ways to define abstract classes in Python. I read the documentation and also could not find an answer here on stackoverflow.

The main difference between the three examples (see code below) is:

  • A sets a new metaclass abc.ABCMeta explicitly
  • B inherits from abc.ABC
  • C inherits from objects but defines @abc.abstractmethod classes

It seems that A and B are not different (i.e. also B has the new metaclass abc.ABCMeta). However, class C remains of type type.

What are the impacts of not defining a metaclass for C? When is it necessary to define the metaclass or is it wrong/bad style to not define the abc.ABCMeta metaclass for an abstract class? Nonetheless, the class C seems to behave as I expect from an ABC.

import abc

class A(metaclass=abc.ABCMeta):
    # Alternatively put __metaclass__ = abc.ABCMeta here
    @abc.abstractmethod
    def foo(self):
        raise NotImplementedError


class B(abc.ABC):

    @abc.abstractmethod
    def foo(self):
        raise NotImplementedError


class C(object):

    @abc.abstractmethod
    def foo(self):
        raise NotImplementedError


class Aimpl(A):

    def foo(self):
        print("Aimpl")


class Bimpl(B):
    def foo(self):
        print("Bimpl")


class Cimpl(C):
    #def foo(self):
     #   print("Cimpl")
     pass

Aimpl().foo()                           # Aimpl
print(isinstance(Aimpl, A))             # False
print(issubclass(Aimpl, A))             # True
print(isinstance(Aimpl, abc.ABCMeta))   # True
print(type(A))                          # <class 'abc.ABCMeta'>
print("---")

Bimpl().foo()                           # Bimpl
print(isinstance(Bimpl, B))             # False
print(issubclass(Bimpl, B))             # True
print(isinstance(Bimpl, abc.ABCMeta))   # True
print(type(B))                          # <class 'abc.ABCMeta'>
print("---")

Cimpl().foo()                           # Cimpl
print(isinstance(Cimpl, C))             # False
print(issubclass(Cimpl, C))             # True
print(isinstance(Cimpl, abc.ABCMeta))   # False
print(type(C))                          # <class 'type'>
print("---")
1
  • "Nonetheless, the class C seems to behave as I expect from an ABC." – Meaning what exactly? What do you expect and how does it behave? Commented May 29, 2018 at 7:53

1 Answer 1

8

The abc.ABCMeta class is necessary to actually enforce the abstractmethod behaviour. Its itention is to disallow instantiation of any classes which do not implement the abstract method. The decorator itself cannot enforce that, the metaclass is enforcing the decorator upon instantiation:

class Foo:
    @abstractmethod
    def bar(self):
        pass

Foo()  # works

However:

class Foo(metaclass=ABCMeta):
    @abstractmethod
    def bar(self):
        pass

Foo()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: Can't instantiate abstract class Foo with abstract methods bar

So, without the metaclass, the abstractmethod decorator doesn't do anything.

abc.ABC is merely a shorthand so you can do Foo(ABC) instead of Foo(metaclass=ABCMeta), that is all:

A helper class that has ABCMeta as its metaclass. With this class, an abstract base class can be created by simply deriving from ABC avoiding sometimes confusing metaclass usage [..]

https://docs.python.org/3/library/abc.html#abc.ABC

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.