It is used in python, in the form (usually) of abstract classes.
The purpose varies, in java they solve multiple inheritance, in python they act as a contract between 2 classes.
Class A does something and a part of that something involves class B. Class B can be implemented in several ways, so instead of making 10 different classes and HOPE they will be used properly, you make them inherit from an abstract class (an interface) and you ensure they MUST implement all the methods defined as abstract. (NOTE IF THEY DON'T IMPLEMENT ANY OF THE METHODS IT WILL CRASH AT BUILD TIME, WHEN YOU INSTALL THE PACKAGE, NOT WHEN YOU RUN IT WHICH IS VERY IMPORTANT IN MIDDLE/LARGE-SIZE PROJECTS).
You also know that ANY class that implements those methods will work with the class that uses it. This sounds trivial, but the business side LOVES it because it means you can outsource part of your code and it will connect and work with the rest of your code.
From what I understand, Interfaces tell a class what it can do, not how to do it. This means at some point the class has to be told how to do the methods.
They actually tell it what it MUST do, as to how they do it we don't care.
If this is the case, what's the point in interfaces? Why not just have the definition of the method in the class?
That is not the point but yes, absolutely you need the definition of the method in the class that inherits from the interface.
Lets give you a more concrete example.
Imagine you have a python framework that runs some tasks. The tasks can run locally (in the same computer where the python framework is running), they can run on a distributed system, by submitting them to some central scheduler, they can run in a docker container, on Amazon Web services .... you get the idea.
All you need is an interface (an abstract class in python) that has a run_task method, as to which one you use is up to you.
e.g:
class Runner:
__metaclass__ = abc.ABCMeta
@abstractmethod
def run_task(self, task_command):
return
class LocalRunner(Runner):
def run_task(self, task_command):
subprocess.call(task_command)
class SlurmRunner(Runner):
def run_task(self, task_command):
subprocess.call('sbatch ' + task_command)
Now thge important bit, as you may be asking Why the ^$^$% do I need all these
complications? (probably you don't if yopur project is small enough, but there is a breakpoint depending on size where you almost HAVE to start using these things).
The class that uses the runner ONLY needs to understand the interface, e.g. you have a Task class, that class can delegate the execution of the task to the TaskRunner, as to which one is implemented you DON'T care, they are in a sense polymorphic.
class Task:
def __init__(self, task_runner):
self.task_runner = task_runner
self.task_command = 'ls'
def run_this_task(self):
self.task_runner.run_task(self.task_command)
And, if you are some programmer your boss can tell you, I need a new class that executes commands on AWS, you apss it a command and it implements a task_runner method , then you DONT need to know ANYTHING about the rest of the code, you can implement this bit as a completely isolated piece (this is the outsourcing part, now you can have a 100 people designign 100 different p[ieces, they don't need to know anything about the code, just the interfaces).