Interfaces and Protocols
Interface
An interface is a programming structure that allowed us to enforce certain properties on an object/ class. E.g. say we have a car class and scooter class and a truck class. Each of the three classes should have a start_engine() method.
Duck Typing
In runtime invoke methods we expect the method to have (occurs at runtime).
For safety checks we use the try except approach or the hasattr.
We often hear about file like object or an iterable. If an object has a read
method, it can be treated as a file like object., if the object has an __iter__
magic method, it is an iterable. So any object can conform to a certain interface just by implementing the expected behaviour (methods).
Magic methods
Magic methods belonging to a class, is an informal interface (to enforce certain properties on an object or class). It makes the object conform to protocols. We can implement a protocol by implementing the methods expected by it.
You can think of protocols as enforced properties for an object / class.
class Team:
def __init__(self, members):
self.__members = members
def __len__(self):
return len(self.__members)
def __contains__(self, member):
return member in self.__members
def __iter__(self):
for member in self.__members:
yield member # the yield allows the object to become an iterable.
justice_league = Team(["batman", "wonder women", "flash"])
# Sized protocol
print(len(justice_league))
# Container protocol
print("batman" in justice_league)
print(type(justice_league))
for member in justice_league:
print(member)
3
True
<class '__main__.Team'>
batman
wonder women
flash
Formal interfaces ABCS
Informal interfaces or duck typing can cause confusion. For example, Bird
and Aeroplane
both can fly()
. But they are not the same, even if they implement the same (informal) interface / protocols. Abstract Base Classes (ABCs) can help solve this issue.
Any objects deriving from these base classes are forced to implement those methods. This is equivalent to a interface.
If helps us enforce other developers to realise when deriving from this “interface” they are made to define the necessary methods. This is especially useful for frameworks or you want to make sure rules are being established.
Benefit of interfaces
- enforce behaviour for other developers to follow
- catch misspelled or forgotten methods to catch bugs.
import abc
class Bird(abc.ABC):
@abc.abstractmethod
def fly(self):
pass
# now if any class derives from our base `Bird` class, it must implement the `fly` method too.
class Parrot(Bird):
pass
p = Parrot()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-20-d76f7476ac6d> in <module>()
2 pass
3
----> 4 p = Parrot()
TypeError: Can't instantiate abstract class Parrot with abstract methods fly
class Parrot(Bird):
def fly(self):
pass
p = Parrot()
print(isinstance(p, Bird)) # parrot class is recognized as an instance of Bird ABC`
True
class Aeroplane(abc.ABC):
@abc.abstractmethod
def fly(self):
pass
class Boeing(Aeroplane):
def fly(self):
pass
b = Boeing()
print(isinstance(p, Aeroplane))
print(isinstance(b, Bird))
False
False
You can see that even though objects have the same methods, we can tell the difference between the Aeroplane interface and Boeing interface.
It is often discouraged to create custom ABCs, but instead use the build-in ones. Before writing you own ABC check if there’s an ABC for the same purpose in the standard library
https://docs.python.org/3/library/collections.abc.html#module-collections.abc