When talking about static type checking, it helps to understand the notion of a subtype as distinct from a subclass. (In Python, type and class are synonymous; not so in the type system implemented by tools like mypy.)

A type T is a nominal subtype of type S if we explicitly say it is. Subclassing is a form of nominal subtyping: T is a subtype of S if (but not only if) T is a subclass of S.

A type T is a structural subtype of type S if it something about T itself is compatible with S. Protocols are Python's implementation of structure subtyping. Shape does not not need to be a nominal subtype of IShape (via subclassing) in order to be a structural subtype of IShape (via having an x attribute).

So the point of defining IShape as a Protocol rather than just a superclass of Shape is to support structural subtyping and avoid the need for nominal subtyping (and all the problems that inheritance can introduce).

class IShape(Protocol):
    x: float


# A structural subtype of IShape
# Not a nominal subtype of IShape
class Shape:
    def __init__(self):
        self.x = 3

# Not a structural subtype of IShape
class Unshapely:
    def __init__(self):
        pass


def foo(v: IShape):
    pass

foo(Shape())  # OK
foo(Unshapely())  # Not OK

So is structural subtyping a replacement for nominal subtyping? Not at all. Inheritance has its uses, but when it's your only method of subtyping, it gets used inappropriately. Once you have a distinction between structural and nominal subtyping in your type system, you can use the one that is appropriate to your actual needs.

Answer from chepner on Stack Overflow
🌐
Turingtaco
turingtaco.com › protocols-default-methods-inheritance-and-more
Protocols: Default Methods, Inheritance, and More
December 7, 2024 - By the end of this article, We'll ... code. A default implementation in a Protocol allows the definition of methods with a concrete implementation directly within the Protocol....
🌐
Python
typing.python.org › en › latest › spec › protocol.html
Protocols — typing documentation
In this case a class could use default implementations of protocol members. Static analysis tools are expected to automatically detect that a class implements a given protocol.
Discussions

python - Correct way to hint that a class is implementing a Protocol? - Stack Overflow
On a path of improvement for my Python dev work. I have interest in testing interfaces defined with Protocol at CI/deb building time, so that if a interface isn't actually implemented by a class we... More on stackoverflow.com
🌐 stackoverflow.com
How to define a default implementation for a `Protocol` property with a setter
I hope this question is not too basic. Consider the case in which I want to provide a Protocol with a default implementation that performs some action on the setter of one of its properties (so it ... More on github.com
🌐 github.com
0
October 25, 2023
Interfaces with Protocols: why not ditch ABC for good?
Whereas with Protocols it's gonna be ( good tutorial ): I think that is not a good example of how to write programs. What he did by having protocols I would have done by using mixins. The way that I see objects is that they have various capabilities that can be mixed in. multiple inheritance in python would have been a much better way to implement that example in my opinion. I would also say that the author of this tutorial needs to learn a thing or 2 about an inversion of control and dependency injection. The author basically sets up a straw man problem and then solves his straw man problem. He had no business creating instances of the object outside of the class itself. If he had simply called a constructor methods within the classes then the other class wouldn't have been attempting to make instances of those other classes. More on reddit.com
🌐 r/Python
34
63
January 22, 2023
What to use in replacement of an interface/protocol in python - Stack Overflow
I am making a chess game and wanted ... interface/protocol. Python does not have those in the language, so what am I supposed to use? I read a bit about factories, but I'm not sure how they would help. Thanks in advance! ... What would be the point of interfaces in Python, as it could not force you to implement all the ... More on stackoverflow.com
🌐 stackoverflow.com
July 26, 2023
🌐
Mypy
mypy.readthedocs.io › en › stable › protocols.html
Protocols and structural subtyping - mypy 1.19.1 documentation
Predefined protocol reference lists various protocols defined in collections.abc and typing and the signatures of the corresponding methods you need to define to implement each protocol. ... typing also contains deprecated aliases to protocols and ABCs defined in collections.abc, such as Iterable[T]. These are only necessary in Python 3.8 and earlier, since the protocols in collections.abc didn’t yet support subscripting ([]) in Python 3.8, but the aliases in typing have always supported subscripting.
🌐
Python
peps.python.org › pep-0544
PEP 544 – Protocols: Structural subtyping (static duck typing) | peps.python.org
March 5, 2017 - In order to make everything a protocol by default, and have isinstance() work would require changing its semantics, which won’t happen. Protocol classes should generally not have many method implementations, as they describe an interface, not an implementation.
Top answer
1 of 1
22

When talking about static type checking, it helps to understand the notion of a subtype as distinct from a subclass. (In Python, type and class are synonymous; not so in the type system implemented by tools like mypy.)

A type T is a nominal subtype of type S if we explicitly say it is. Subclassing is a form of nominal subtyping: T is a subtype of S if (but not only if) T is a subclass of S.

A type T is a structural subtype of type S if it something about T itself is compatible with S. Protocols are Python's implementation of structure subtyping. Shape does not not need to be a nominal subtype of IShape (via subclassing) in order to be a structural subtype of IShape (via having an x attribute).

So the point of defining IShape as a Protocol rather than just a superclass of Shape is to support structural subtyping and avoid the need for nominal subtyping (and all the problems that inheritance can introduce).

class IShape(Protocol):
    x: float


# A structural subtype of IShape
# Not a nominal subtype of IShape
class Shape:
    def __init__(self):
        self.x = 3

# Not a structural subtype of IShape
class Unshapely:
    def __init__(self):
        pass


def foo(v: IShape):
    pass

foo(Shape())  # OK
foo(Unshapely())  # Not OK

So is structural subtyping a replacement for nominal subtyping? Not at all. Inheritance has its uses, but when it's your only method of subtyping, it gets used inappropriately. Once you have a distinction between structural and nominal subtyping in your type system, you can use the one that is appropriate to your actual needs.

🌐
GitHub
github.com › python › typing › issues › 1497
How to define a default implementation for a `Protocol` property with a setter · Issue #1497 · python/typing
October 25, 2023 - python / typing Public · Notifications · You must be signed in to change notification settings · Fork 282 · Star 1.7k · New issueCopy link · New issueCopy link · Closed · Closed · How to define a default implementation for a Protocol property with a setter#1497 ·
Author   vnmabus
🌐
Real Python
realpython.com › python-protocol
Python Protocols: Leveraging Structural Subtyping – Real Python
July 25, 2024 - Otherwise, if you define instance attributes inside instance methods, which is a common practice in Python, then you’ll get an error from your type checker. Next up, you have different types of methods. In this example, none of the methods have a defined implementation. However, you can have protocol methods with default implementations.
Find elsewhere
🌐
Python
typing.python.org › en › latest › reference › protocols.html
Protocols and structural subtyping — typing documentation
Note that inheriting from an existing ... ABC that implements the given protocol (or protocols). The Protocol base class must always be explicitly present if you are defining a protocol: class NotAProtocol(SupportsClose): # This is NOT a protocol new_attr: int class Concrete: new_attr: int = 0 def close(self) -> None: ... # Error: nominal subtyping used by default x: NotAProtocol ...
🌐
Reddit
reddit.com › r/python › interfaces with protocols: why not ditch abc for good?
r/Python on Reddit: Interfaces with Protocols: why not ditch ABC for good?
January 22, 2023 -

Hello, if one finds interfaces useful in Python (>=3.8) and is convinced that static type-checking is a must, then why not ditch ABC and always use Protocols? I understand that the fundamental idea of a protocol is slightly different from an interface, but in practice, I had great success replacing abc's with Protocols without regrets.

With abc you would write (https://docs.python.org/3/library/abc.html) :

from abc import ABC, abstractmethod

class Animal(ABC):
   @abstractmethod
   def eat(self, food) -> float:
       pass

Whereas with Protocols it's gonna be (good tutorial):

from typing import Protocol

class Animal(Protocol):
   def eat(self, food) -> float:
       ...

Scores in my subjective scoring system :)

CapabilityABCProtocols
Runtime checking11 (with a decorator)
Static checking with mypy11
Explicit interface (class Dog(Animal):)11
Implicit interface with duck-typing (class Dog:)0.5 (kind of with register, but it doesn't work with mypy yet)1
Default method implementation (def f(self): return 5)-1 (implementations shouldn't be in the interfaces)-1 (same, and mypy doesn't catch this)
Callback interface01
Number of code lines-1 (requires ABC inheritance and abstracmethod for every method)0 (optionalProtocol inheritance)
Total score1.54

So I do not quite see why one should ever use ABC except for legacy reasons. Other (IMHO minor) points in favour of ABC I've seen were about interactions with code editors.

Did I miss anything?

I put more detailed arguments into a Medium. There are many tutorials on using Protocols, but not many on ABC vs Protocols comparisons. I found a battle of Protocols vs Zope, but we are not using Zope, so it's not so relevant.

Top answer
1 of 8
65

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+.

2 of 8
21

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.

🌐
Auth0
auth0.com › blog › protocol-types-in-python
Protocol Types in Python 3.8
June 23, 2021 - While the concept is the same as Clojure, Python does not need explicit protocol declaration. If a type has the methods specified in the protocol, then it implements the protocol.
🌐
Xebia
xebia.com › home › blog › protocols in python: why you need them
Protocols In Python: Why You Need Them | Xebia
July 25, 2022 - Any class that implements the required methods and attributes can be considered a subtype of the protocol. Can Python protocols be used at runtime? By default, protocols are only checked at compile-time.
🌐
Andrewbrookins
andrewbrookins.com › technology › building-implicit-interfaces-in-python-with-protocol-classes
Building Implicit Interfaces in Python with Protocol Classes – Andrew Brookins
July 5, 2020 - Python 3.5 defined the methods in each protocol formally, but did so using abstract base classes in the collections.abc module. If you wanted to hook into a protocol, you could use an ABC as documentation – but you couldn’t use mypy to check that an object implemented the protocol unless ...
🌐
Hacker News
news.ycombinator.com › item
Where would you use `typing.Protocol` where you wouldn't use an abstract base cl... | Hacker News
March 19, 2021 - The only time I've ever used `Protocol` is to define a type that makes it explicit that I need an object to have a `__str__` implementation: · Abstract base classes require everything to extend from a base-level object, and also inherit it's default implementations.
🌐
Python.org
discuss.python.org › ideas
Functional Protocols - Ideas - Discussions on Python.org
January 13, 2023 - Topic Introduction Has anyone ever considered adding protocols to Python that fit the more functional style? For example, we already have the built-in len which simply calls the __len__ magic method on implementers of the “Sized” protocol. Let’s say I want to create a new protocol, ...
🌐
GitHub
github.com › python › typing › issues › 11
Protocols (a.k.a. structural subtyping) · Issue #11 · python/typing
October 15, 2014 - In mypy's typing,py there's a neat feature called Protocol. Perhaps we should add this to the PEP. Or perhaps it's similar to a Python ABC?
Author   gvanrossum
🌐
GitHub
github.com › python › mypy › issues › 8395
Treat methods on protocols with empty bodies as abstract · Issue #8395 · python/mypy
However, this leads to less elegant code and additional runtime checks. Since it seems kind of obvious that ... is never a default implementation of a method, I would suggest to treat it the same way as protocol methods decorated with @abstractmethod.
🌐
mypy
mypy.readthedocs.io › en › latest › protocols.html
Protocols and structural subtyping - mypy 1.20.0+dev.0e6cfd41cf883acfada2ed2c72dc6f18a343bb78 documentation
February 21, 2024 - Predefined protocol reference lists various protocols defined in collections.abc and typing and the signatures of the corresponding methods you need to define to implement each protocol. ... typing also contains deprecated aliases to protocols and ABCs defined in collections.abc, such as Iterable[T]. These are only necessary in Python 3.8 and earlier, since the protocols in collections.abc didn’t yet support subscripting ([]) in Python 3.8, but the aliases in typing have always supported subscripting.
🌐
Twistedmatrix
glyph.twistedmatrix.com › 2021 › 03 › interfaces-and-protocols.html
Interfaces and Protocols - Deciphering Glyph - Twisted
March 15, 2021 - The net result of this is that ... use Python 3.7, where Protocol is built in to typing) you have to take a type-check-time dependency on the typing_extensions package, whereas with zope.interface you’ll need both the run-time dependency of zope.interface itself and the Mypy plugin at type-checking time. So in a situation where both are roughly equivalent, Protocol tends to win by default...
🌐
GitConnected
levelup.gitconnected.com › python-interfaces-choose-protocols-over-abc-3982e112342e
Python interfaces: abandon ABC and switch to Protocols | by Oleg Sinavski | Level Up Coding
January 19, 2023 - Abstract base classes sometimes tend to acquire default method implementations. This is what it might look like: 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. In that case, they stop being “abstract” and become just base classes. Python and static checkers do not catch that.