visit
I used a standard Python library abc
to define interfaces for the last 10 years of my career. But recently, I found that relatively new Python Protocols are way nicer.
abc
typing.Protocols
third-party implementations like Zope
abc
package is probably the most popular:
from abc import ABC, abstractmethod
class Animal(ABC):
@abstractmethod
def eat(self, food) -> float:
pass
@abstractmethod
def sleep(self, hours) -> float:
pass
from zope.interface import Interface
class Animal(Interface):
def eat(self, food) -> float:
pass
def sleep(self, hours) -> float:
pass
from typing import Protocol
class Animal(Protocol):
def eat(self, food) -> float:
...
def sleep(self, hours) -> float:
...
Maybe protocols and interfaces are theoretically different beasts, but a protocol does the job.
I had great success replacing abc
with Protocols
without any downsides.
So I would immediately dismiss any custom implementations or Zope
.
It’s an extra dependency you have to deal with forever: installation, versions, support, and so on. For example, you have to install a to support a zope.interface
well. Additionally, a new developer in the team might not know this custom package, and you'll have to explain what it is and why you chose it. The main battle will happen between abc
and Protocols
.
But if you really want a zope
vs Protocols
battle, please
(it has a detailed analysis of the runtime benefits of zope
).
The big assumption I’m going to make is that you’re already convinced that static checking is a must:
you are not going to run the code that fails pylint/mypy
.
Both checkers support abc
and Protocols
equally well.
Also, just know that both abc
and Protocols
allow runtime checking, in case you need it.
First, note that you still can explicitly inherit from an abc
and a Protocol
. Many arguments in
a (and comments) from revolve around the misconception
that you can’t do that with protocols.
class Giraffe(Animal):
...
So in that regard, abc
and protocols
could be used the same way.However, Protocols give you an extra degree of design freedom by default.
class Giraffe: # no base class needed!
def eat(self, food) -> float:
return 0.
def sleep(self, hours) -> float:
return 1.
def feed_animal(animal: Animal):
...
giraffe = Giraffe()
feed_animal(giraffe)
Protocols do not force you to opt-in, but you can establish a company-wide rule to explicitly inherit from any protocol.
abc
also support implicit interfaces through the concept of .
But you have to call register
for every implementation:
class Giraffe: # no base class needed!
def eat(self, food) -> float:
return 0.
class Animal(ABC):
...
Animal.register(Giraffe) # achieves the same as implicit Protocol
Procotol
supports implicit and explicit variants without extra syntax and works with mypy
.
Also, mypy
does not support register
as of the end of 2022.
I'm not sure if we can fully count that in favor of abc
.
Protocols allow you to define an interface for a function (not only a class).
It is a very cool feature that is worthy of a separate post.Unfortunately, there is a big downside to both abc
and Protocols
. In the real world, many people work in a single codebase. Abstract base classes sometimes tend to acquire default method implementations.
class Animal(Protocol): # the same holds for Animal(ABC):
def eat(self, food) -> float:
... # this is still abstract
def sleep(self, hours) -> float:
return 3.
Last but not least, you can count the number of lines of code you need to define an interface.
With abc
, you must have an abstractmethod
decorator for every method.
But with Protocols without runtime checking, you don't have to use any decorators at all.
So here, Protocols win hands down.
Capability | ABC | Protocols |
---|---|---|
Runtime checking | 1 | 1 |
Static checking | 1 | 1 |
Explicit interface with inheritance | 1 | 1 |
Implicit interface without inheritance ( | 0.5 | 1 |
Ability to have default method implementation | -1 | -1 |
Callback interface | 0 | 1 |
Number of lines | -1 | 0 |
|
|
|
Total | 1.5 |
4 |
Hopefully, I’m not missing anything huge in this analysis. Thank you for reading! Looking at the results, team "Protocols" wins, and you probably should just start using it!
Thank you for reading! You can find me on or .
Originally published