Protocols vs Abstract Base Classes in Python
How do I annotate that a python class should implement a Protocol? - Stack Overflow
@typing.protocol decorator to create a class of type Protocol - Ideas - Discussions on Python.org
python - Correct way to hint that a class is implementing a Protocol? - Stack Overflow
Videos
Hi everyone. Last time I shared a post about Interface programming using abs in Python, and it got a lot of positive feedbackโthank you!
Several people mentioned protocols, so I wrote a new article exploring that topic. In it, I compare protocols with abstract base classes and share my thoughts and experiences with both. You can check it out here: https://www.tk1s.com/python/protocols-vs-abstract-base-classes-in-python Hope you'll like it! Thanks!
New in Python 3.8:
Some of the benefits of interfaces and protocols are type hinting during the development process using tools built into IDEs and static type analysis for detection of errors before runtime. This way, a static analysis tool can tell you when you check your code if you're trying to access any members that are not defined on an object, instead of only finding out at runtime.
The typing.Protocol class was added to Python 3.8 as a mechanism for "structural subtyping." The power behind this is that it can be used as an implicit base class. That is, any class that has members that match the Protocol's defined members is considered to be a subclass of it for purposes of static type analysis.
The basic example given in PEP 544 shows how this can be used.
Copyfrom typing import Protocol
class SupportsClose(Protocol):
def close(self) -> None:
# ...
class Resource:
# ...
def close(self) -> None:
self.file.close()
self.lock.release()
def close_all(things: Iterable[SupportsClose]) -> None:
for thing in things:
thing.close()
file = open('foo.txt')
resource = Resource()
close_all([file, resource]) # OK!
close_all([1]) # Error: 'int' has no 'close' method
Note: The typing-extensions package backports typing.Protocol for Python 3.5+.
In short, you probably don't need to worry about it at all. Since Python uses duck typing - see also the Wikipedia article for a broader definition - if an object has the right methods, it will simply work, otherwise exceptions will be raised.
You could possibly have a Piece base class with some methods throwing NotImplementedError to indicate they need to be re-implemented:
Copyclass Piece(object):
def move(<args>):
raise NotImplementedError(optional_error_message)
class Queen(Piece):
def move(<args>):
# Specific implementation for the Queen's movements
# Calling Queen().move(<args>) will work as intended but
class Knight(Piece):
pass
# Knight().move() will raise a NotImplementedError
Alternatively, you could explicitly validate an object you receive to make sure it has all the right methods, or that it is a subclass of Piece by using isinstance or isubclass.
Note that checking the type may not be considered "Pythonic" by some and using the NotImplementedError approach or the abc module - as mentioned in this very good answer - could be preferable.
Your factory just has to produce instances of objects having the right methods on them.