how.wtf

ABC vs Protocol in Python

· Thomas Taylor

differences between ABC and Protocol

Before typing was released for Python, the ABC class reigned as champion for describing shape and behaviors of classes. After type annotations, ABC and @abstractmethod were still used to describe the behaviors: they felt ‘interface-like’.

Then, Protocol was released and introduced a new way for declaring class behaviors.

Should you use ABC or Protocol?

Before describing why or why not you should choose one over the other, let’s delve into what each of them are.

What is ABC or Abstract Base Classes?

Abstract Base Classes, or ABC, is a a module that provides infrastructure to define abstract classes in Python.

As previously stated, they predated type hinting and were the go-to for enforcing class behaviors. Their goal was to provide a standardized means to test if an object adhered to a given specification.

For example,

 1from abc import ABC, abstractmethod
 2
 3
 4class Readable(ABC):
 5    @abstractmethod
 6    def read(self):
 7        pass
 8
 9
10class Stream(Readable):
11    def read(self):
12        return "read!"
13
14
15class FileReader(Readable):
16    def read(self):
17        return "some lines from a file"

ABCs use nominative subtyping by default: ie. you must explicitly inherit from a superclass to be considered a subtype.

Although, they do have a register method that enables a structural-like typing:

 1from abc import ABC, abstractmethod
 2
 3
 4class Readable(ABC):
 5    @abstractmethod
 6    def read(self):
 7        pass
 8
 9
10class Stream:
11    def read(self):
12        return "read!"
13
14
15Readable.register(Stream)

What is Protocol

Protocols are intended to make static checking easier - without needing to use an isinstance check at runtime.

They are treated as formalized duck typing - ie. the class only has to have the same attributes and methods… NOT be an instanceof.

Taking our class from earlier,

1from typing import Protocol
2
3
4class Readable(Protocol):
5    def read(self) -> str: ...

We can implement the Protocol in different ways:

1class Stream:
2    def read(self) -> str:
3        return "read!"

OR explicitly

1class Stream(Readable):
2    def read(self) -> str:
3        return "read!"

Additionally, we can enforce runtime evaluation using the runtime_checkable decorator:

 1from typing import Protocol, runtime_checkable
 2
 3
 4@runtime_checkable
 5class Readable(Protocol):
 6    def read(self) -> str: ...
 7
 8
 9class Stream(Readable):
10    def read(self) -> str:
11        return "read!"

Which is preferred?

From my perspective, Protocol wins. Given Python’s emphasis on duck typing (“if it walks and quacks like a duck, then it must be a duck”), it makes sense to opt for Protocol.

If there is a need to enforce runtime checks, it has that covered with the @runtime_checkable.

Protocol Pros:

Protocol Cons:

#python  

Reply to this post by email ↪