Home Python’s Abstract Base Classes (ABC) and Interfaces Explained (With Code Snippets)
Post
Cancel

Python’s Abstract Base Classes (ABC) and Interfaces Explained (With Code Snippets)

When you get started with object-oriented programming, you will come across interfaces and abstract classes. Especially languages like Java or C# make use of those two concepts a lot to structure complex programming projects and to leverage abstraction. However, in Python, interfaces and abstract classes aren’t part of the standard languages’ definition. In this article, you will learn what interfaces and abstract classes are and how to use Python’s ABC module to use those two concepts in your Python code.

What are Interfaces and Abstract Classes in a Programming Language?

Interfaces in Object-oriented Programming Languages

In general terms, an interface is the definition of the inputs and outputs of a thing. For example, the common inputs and outputs of water kettles are:

  • An inlet for cold water.
  • A button to start boiling the water
  • An outlet for hot water.

However, each water kettle manufacturer will “implement” those inputs and outputs differently. Some have a large lid to fill in the water while others have an only small hole, or some water kettles have their button on the top while others have their button on the bottom. An interface does not specify the implementation, it only specifies the inputs and outputs of something.

An in object-oriented programming languages interface also acts as a description of which methods a class has and what the input parameters and outputs of those methods are. To create an instance that implements an interface, there must be a class that implements the interface. There are no instances of interfaces.

Abstract classes in Object-oriented Programming Languages

In contrast to interfaces, an abstract class not only defines the inputs and outputs, it also defines the behavior. Coming back to the water kettle example, an abstract water kettle class would provide a method that implements how water is boiled. Regular classes can inherit from abstract classes to make use of the implementation of an abstract class.

Interfaces and Abstract Classes in Java

After this short theory block, let’s take a look at Java, which a very pedantic object-oriented programming language.

First define an abstract class called Animal. This abstract class has the String attribute name, a default constructor to set name, and a method walk which implements a default “walking” behavior:

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class Animal {

    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public void walk() {
        System.out.println(this.name + " walks.");
    }
}

It makes sense to define Animal as an abstract class because there is no “Animal” in the real world, there are only specializations of animals such as dogs, cats, horses, etc. Therefore, Animal cannot be instantiated.

Using the abstract class Animal allows you to implement a Dog class which already comes with the common attribute name and the method walk, additionally the method wag_tail is added, which is common among all dogs:

1
2
3
4
5
6
7
8
9
public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    public void wag_tail() {
        System.out.println(this.name + " wags its tail.");
    }
}

Some animals might have special abilities such as talking, but how they talk is different across different animals. A Talking interface allows you to specify the talk method together with its inputs and outputs without actually implementing it:

1
2
3
public interface Talking {
    public void talk(String str);
}

Now you can define a TalkingDog class which inherits from Dog and implements the Talking interface. While inheriting from the Dog class provides all the common attributes and methods of Dogs, implementing the Talking interface requires you to implement the talk method:

1
2
3
4
5
6
7
8
9
public class TalkingDog extends Dog implements Talking {
    public TalkingDog(String name) {
        super(name);
    }

    public void talk(String str) {
        System.out.println(this.name + " barks " + "\"" + str + "\".");
    }
}

Putting everything together in a Java Main class (yes, everything in Java is a class) lets you compile and run the code using interfaces and abstract classes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Main {
    public static void main(String[] args) {

        // abstract classes can not be instantiated
        // Animal bob = new Animal("bob");
        // System.out.println(bob.name);

        Dog pluto = new Dog("Pluto");
        pluto.walk();
        pluto.wag_tail();

        TalkingDog goofy = new TalkingDog("Goofy");
        goofy.walk();
        goofy.wag_tail();
        goofy.talk("Hello");
    }
}

To compile the code above, use the following commands:

1
2
javac *.java
javaa -cp . Main

Why are there no Interfaces and Abstract Classes in Python by Default (Duck Typing Explained)?

Now that you know how interfaces and abstract classes are used in object-oriented languages such as Java, let us take a look at Python. Python is a multi-paradigm programming language which has object-oriented features such as classes and inheritance. However, by default, Python doesn’t have interfaces and abstract classes. The reason for this can largely be attributed to the fact that Python is an interpreted language and doesn’t need a compiler to run your code.

In a compiled language such as C++, the compiler turns the code you write into machine instructions. For example, at the time of compilation, it is checked if the arguments passed to a function are actually of the correct type stated in the method signature this allows for much faster code execution because at runtime there are no checks if the arguments that are passed to a method are compatible. This is why languages like C++ or Java are often called strictly typed languages because it is not possible to pass the wrong argument type to a method.

Python uses a concept called duck typing. By default, Python methods accept every type for every input, and there are no checks at runtime if the type is actually correct, only if the number of arguments passed is checked. Only when an attribute of a Python object is accessed or a method is called, the interpreter checks if those are actually available.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Duck:
    def quack(self):
        print("Duck quacks")


class Cat:
    def meow(self):
        print("Cat meows")

counter = 0

def make_it_quack(duck):
    global counter

    if counter > 0:
        duck.quack()
    else:
        counter += 1

In the code above, there are the two classes Duck and Cat, both with their respective methods quack() and meow(). The function make_it_quack(duck) has the parameter duck as an input and will call the quack() method of duck when the counter is greater than 0. When calling the make_it_quack function the first time and passing an object of type Cat everything works fine. However, the second time, when the counter is incremented, the Python interpreter will complain with the following error message:

1
AttributeError: 'Cat' object has no attribute 'quack'

This is because, only at the time of calling the method, it is checked if an object provides this method.

You can also check yourself which methods and attributes an object has at runtime using the built-in dir() function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
>>> c = Cat()
>>> dir(c)

['__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getstate__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 'meow']

The Python interpreter uses this dir() function at runtime to check if an object can be used for the given purpose. This is also called the Duck Test, which states:

“If it walks like a duck and it quacks like a duck, then it must be a duck”

Which means the type of an object is almost completely irrelevant because as long as the object provides the correct methods and attributes it can be used regardless of its type.

How to Implement an Abstract Class in Python?

Even though Python uses duck typing, it can be beneficial to make use of abstract classes and interfaces in complex software projects to communication the abilities of objects. Therefore the Python module abc (short for abstract base classes) provides base classes and decorators to implement abstract classes and interfaces

The following code shows how an abstract Animal class can be implemented using abc:

1
2
3
4
5
6
7
8
from abc import ABC

class Animal(ABC):
    def __init__(self, name):
        self.name = name

    def walk(self):
        print(f"{self.name} walks")

From abc the class ABC is imported, which is the Abstract Base Class from which all abstract classes inherit. The methods and attributes of an abstract class in Python can be defined as in any other regular class.

Inheriting from an abstract Python class works as with any other class in Python:

1
2
3
4
5
6
class Dog(Animal):
    def __init__(self, name):
        super().__init__(name)

    def wags_tail(self):
        print(f"{self.name} wags tail")

And in contrast to Java, an abstract class can also be instantiated in Python without the need for inheriting from it first. This means that abstract classes in Python have more decorative purposes rather than real functionality. However, it can be useful to have abstract classes to tell other developers who want to use your code that this class is not intended for instantiation on its own.

How to Implement an Interface in Python

While abstract classes in Python can be instantiated, interfaces can’t. Defining an interface in Python is also done by inheriting from ABC. But in contrast to an abstract class, all methods of an interface are decorated using @abstractmethod:

1
2
3
4
5
6
from abc import ABC, abstractmethod

class Talking(ABC):
    @abstractmethod
    def talk(self):
        pass

When inheriting from an interface (e.g. implementing it) the @abstractmethod decorator ensures that all methods decorated with it are overridden by the implementing class. It is possible to give a method decorated with @abstractmethod an implementation in the interface but this implementation is never used because all implementers need to override it.

Putting it all Together

Using the Python abc module, you can now implement abstract classes and interfaces in the same way as in the Java example above. Talking is an interface which provides the method talk(), Animal is an abstract class which implements the attribute name and the method walk(), while Dog inherits from Animal and adds wag_tail(), and TalkingDog inherits from Dog and implements talk() from the interface Talking:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from abc import ABC, abstractmethod

class Talking(ABC):
    @abstractmethod
    def talk(self):
        print("talk")


class Animal(ABC):
    def __init__(self, name):
        self.name = name

    def walk(self):
        print(f"{self.name} walks")


class Dog(Animal):
    def __init__(self, name):
        super().__init__(name)

    def wag_tail(self):
        print(f"{self.name} wags tail")


class TalkingDog(Dog, Talking):
    def __init__(self, name):
        super().__init__(name)

    def talk(self):
        print(f"{self.name} says 'Hello'")

if __name__ == '__main__':

    bob = Animal("bob")
    bob.walk()

    pluto = Dog("pluto")
    pluto.walk()
    pluto.wag_tail()

    goofy = TalkingDog("goofy")
    goofy.walk()
    pluto.wag_tail()
    goofy.talk()

Conclusion

In this article, you have learned what interfaces and abstract classes are in object-oriented programming languages, and how to make use of them in Python to structure your code. Make sure to get the free Python Cheat Sheets in my Gumroad shop. If you have any questions about this article, feel free to join our Discord community to ask them over there.

This post is licensed under CC BY 4.0 by the author.