Interface

Interface#

See the article implementing an interface in python on realpython.org.

An interface is some abstraction that allows you to define what methods must necessarily be implemented in successors.

Problem#

Here is an attempt to explain a problem that is solved by abstract classes.

Suppose we need to implement two toy classes, one printing two strings separated by +, another separated by -. Let’s try to solve this head-on.

class PlusConcatenation():
    def __init__(self, left, right):
        self.left = left
        self.right = right
    def get_output(self):
        return f"{self.left}+{self.right}"

class MinusConcatenation():
    def __init__(self, left, right):
        self.left = left
        self.right = right
    def get_output(self):
        return f"{self.left}-{self.right}"

print(
    "Plus concatenation",
    PlusConcatenation("hello", "world").get_output()
)
print(
    "Minus concatenation",
    MinusConcatenation("hello", "world").get_output()
)
Plus concatenation hello+world
Minus concatenation hello-world

Everything works fine, but we had to repeat the code:

def __init__(self, left, right):
    self.left = left
    self.right = right

In this case, it’s not a big problem. But repeating when there may be many more, and it may be repeated in more places - it will be difficult to maintain.

Basic solution#

That’s where the OOP technique comes in - let’s define a parent class that implements the common functionality of the inheritors, and each inheritor specifies its own functionality.

So here __init__ is common for all Concatenation but get_output have to defined by inheritors.

class Concatenation():
    def __init__(self, left, right):
        self.left = left
        self.right = right
    def get_output(self):
        pass

class PlusConcatenation(Concatenation):
    def get_output(self):
        return f"{self.left}+{self.right}"
class MinusConcatenation(Concatenation):
    def get_output(self):
        return f"{self.left}-{self.right}"
print(
    "Plus concatenation",
    PlusConcatenation("hello", "world").get_output()
)
print(
    "Minus concatenation",
    MinusConcatenation("hello", "world").get_output()
)
Plus concatenation hello+world
Minus concatenation hello-world

Well, we got what we wanted.

But there are still some problems with these approaches, which are not really critical, but you have to keep them in mind. If inherit doesn’t realise that it’s functional, it can’t be considered an implementation of the interface - but the syntax below allows you to create such objects, and then they will even be identified as subclasses of the parent class.

In the following cell we have created a DummyConcatenation which doesn’t implement get_output. The problem is that it’s still a subclass of the `DummyConcatenation’ and its instances can be created freely.

class DummyConcatenation(Concatenation):
    pass

print(
    "issubclass(DummyConcatenation, Concatenation)",
    issubclass(DummyConcatenation, Concatenation)
)
print(
    'DummyConcatenation("hello", "world").get_output()',
    DummyConcatenation("hello", "world").get_output()
)
issubclass(DummyConcatenation, Concatenation) True
DummyConcatenation("hello", "world").get_output() None

abc module#

The abc module is designed to create abstractions in Python. So here is simple example of the example below that uses abs.abstractmethod decorator to define method that must have realisation in heirs. And heirs PlusConcatenation and DummyConcatenation that realise and ignore abstract method.

import abc
class Concatenation(metaclass=abc.ABCMeta):
    def __init__(self, left, right):
        self.left = left
        self.right = right
    @abc.abstractmethod
    def get_output(self, path: str, file_name: str):
        """Load in the data set"""
        raise NotImplementedError

class PlusConcatenation(Concatenation):
    def get_output(self):
        print(f"{self.left}+{self.right}")
class DummyConcatenation(Concatenation):
    pass

Now when we try to create using these hairs - PlusConcatenator works fine, but DummyConcatenation wasn’t even able to create an instance.

PlusConcatenation("hello", "world").get_output()
try:
    DummyConcatenation("hello", "world")
except Exception as e:
    print(e)
hello+world
Can't instantiate abstract class DummyConcatenation with abstract method get_output

But DummyConcatenation is still a subclass of Concatenation according to the issubclass function.

issubclass(DummyConcatenation, Concatenation)
True