DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Jessica Alves
Jessica Alves

Posted on

Interfaces in Python: not truly an interface, just a convention.

In the Python context, different from strongly typed programming languages such as Java, interface isn’t a thing. There is no interface keyword. But you can try to implement something similar to an interface. In Python we could try to represent an interface creating a kind of specialized class which would be like an abstract class that contains only abstract methods. It has no implementations at all, just the definition of abstract methods.
By convention you don't have to but you can use the "Interface" word on the name of the class you created to be the interface. Then it will be explicit that it's an interface.
Like the example below:

class GameInterface:
    def start(self):
        raise NotImplementedError("You must implement the start method.")

    def play(self):
        raise NotImplementedError("You must implement the play method.")

    def reset(self):
        raise NotImplementedError("You must implement the reset method.")
Enter fullscreen mode Exit fullscreen mode

As you can see, it works similarly as an interface but it’s not truly an interface.
Once in Python there are no implements or interface keywords and we’re creating something by convention, to use the interface we created, we will pass it as a parent class. Because in fact it is a class. So we can write something like that:

class Pinball(GameInterface):
    def start(self):
        print("Start Pinball")

    def play(self):
        print("Play Pinball")

    def reset(self):
        print("Reset Pinball")

class Tetris(GameInterface):
    def start(self):
        print("Start Tetris")

    def play(self):
        print("Play Tetris")

    def reset(self):
        print("Reset Tetris")

class PacMan(GameInterface):
    def start(self):
        print("Start Pac-Man")

    def play(self):
        print("Play Pac-Man")

    def reset(self):
        print("Reset Pac-Man")
Enter fullscreen mode Exit fullscreen mode

As I mentioned at the beginning of the post, in other programming languages that interfaces can be really implemented not just as a convention, if you don't implement in the class using the interface one of the interface methods probably you won't even be allowed to go on and run the code because some required method is not implemented yet. Different from our Python example that we need to raise errors in order to warn who is implementing the interface that are methods which need to be implemented.

And a note: if your class will inherit another class as a parent class, besides the β€œinterface” one, it’s important to pass first the actual parent class followed by the class you created to be the interface. So let's say you want to create another parent class that implements many other attributes and methods besides the methods defined in the interface. It's important to pass the classes in the mentioned order:

class GameInterface:
    pass

class BaseGame:
    pass

class Tetris(BaseGame, GameInterface):
    pass
Enter fullscreen mode Exit fullscreen mode

Interface as a Type

Going back to our first example, let's say there is a program to run the games we created. And the program runs a start, a play and a reset method. It doesn't matter which instance game is passed to it to be executed, the program needs to ensure that all games have these methods so it won't crash. And to ensure that, the idea of having an interface is basically using it as a type to be passed to the program that will run the games. Then it will ensure that all required methods are there because all games implement the same interface.
See the example below:

class GameInterface:
    def start(self):
        raise NotImplementedError("You must implement the start method.")

    def play(self):
        raise NotImplementedError("You must implement the play method.")

    def reset(self):
        raise NotImplementedError("You must implement the reset method.")

class Pinball(GameInterface):
    def start(self):
        print("Start Pinball")

    def play(self):
        print("Play Pinball")

    def reset(self):
        print("Reset Pinball")

def run_game(game):
    game.start()
    game.play()
    if some_condition:
      game.reset()

Enter fullscreen mode Exit fullscreen mode

So comparing again to typed languages, in TypeScript for example there is a way you can ensure that the type of the parameter passed to run_game is the expected type. You would have to use the created interface as a type between the parenthesis (like any other data types such as integer, string, etc). In TypeScript you would write something like this:

function run_game(game: Game) {
    game.start()
    game.play()
    if ("some condition") {
        game.reset()
    }
}
Enter fullscreen mode Exit fullscreen mode

where game is a parameter of type Game (which is the Game interface).

In Python we can use a similar syntax, it will not complain.
So we can write something similar to what we already wrote above. In Python it would be like that:

def run_game(game: GameInterface):
    game.start()
    game.play()
    if some_condition:
      game.reset()
Enter fullscreen mode Exit fullscreen mode

So if we create an instance of any of our games and pass it as an argument to the run_code method, it will run perfectly because all of them have the required methods implemented by the GameInterface.

And what if we create a method for a specific class of our games and call it inside our run_game method?

Well in Python there would be no complaint and the program would crash, raising an error if you pass an instance from a class that doesn't have the specific method. But in Java or TypeScript it wouldn't even be allowed to pass the specific method inside the run_code method because it would complain before you run the code. Once the parameter type is defined, Java or TS would enforce that all methods called inside run_code are implemented in the Game interface.
See both examples below:

Python example when you pass an instance from a class containing the specific method:
First Python example

Python example when you pass an instance from a class that doesn't have the specific method. It crashes:

Second Python example

TypeScript example:
TypeScript example

The main thing to notice when comparing the examples above is what I've mentioned: in the first example you see there is no complaint from Python when I write a method that doesn't belong to the specified interface but only to a specific class. The program doesn't crash because I passed an instance from the specific class. But in the second example you can see in the console the error because I passed an instance from a class that doesn't contain the specific method.
And finally, in the third example, you see the difference of using a strongly typed language such as TypeScript that complains when you write a method that doesn't belong to the specified interface. It doesn't even let me run it.

Top comments (0)

Build Anything...


Use any Linode offering to create something for the DEV x Linode Hackathon 2022. A variety of prizes are up for grabs, inculding $1,000 USD. πŸ‘€

β†’ Join the Hackathon <-