As explained by @jonrsharpe, you do not pass an iterable of Constructible instances to map_is but an iterable of classes. That means that you should define the function that way:

def map_is(cs: Iterable[Type[Constructible]], i: int):
  return (C(i) for C in cs)

That is enough for mypy to validate the code.

But there is an unrelated problem: you never declared any __hash__ nor __equal__ special method. That means that in assert values == (X(5), Sq(5)) the equality used is the one defined on the object class (same as is). So after the above fix, the code executes successfully but still raises an AssertionError, because the objects do have same value, yet they are distinct objects...

Answer from Serge Ballesta on Stack Overflow
๐ŸŒ
Python
typing.python.org โ€บ en โ€บ latest โ€บ spec โ€บ protocol.html
Protocols โ€” typing documentation
isinstance() can be used with both data and non-data protocols, while issubclass() can be used only with non-data protocols. This restriction exists because some data attributes can be set on an instance in constructor and this information is not always available on the class object.
๐ŸŒ
Mypy
mypy.readthedocs.io โ€บ en โ€บ stable โ€บ protocols.html
Protocols and structural subtyping - mypy 1.19.1 documentation
In Python 3.9 and later, the aliases in typing donโ€™t provide any extra functionality. You can define your own protocol class by inheriting the special Protocol class:
Discussions

Protocol can't constrain __init__
Bug Report The following code produces an error when var() is called, but not when it is declared. If you can declare __init__ on Protocols, shouldn't implementations have to match, at least wh... More on github.com
๐ŸŒ github.com
3
June 11, 2022
What to use in replacement of an interface/protocol in python - Stack Overflow
I am making a chess game and wanted to make a standard piece 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 ... More on stackoverflow.com
๐ŸŒ stackoverflow.com
python - typing.Protocol class `__init__` method not called during explicit subtype construction - Stack Overflow
Python's PEP 544 introduces typing.Protocol for structural subtyping, a.k.a. "static duck typing". In this PEP's section on Merging and extending protocols, it is stated that The general More on stackoverflow.com
๐ŸŒ stackoverflow.com
Inconsistent Treatment of Class Constructor by Protocol
In the code below we define one protocol which, when called, returns another. In effect we're describing a "factory". However, while MyPy is able to recognize incorrect protocol imple... More on github.com
๐ŸŒ github.com
7
May 16, 2021
๐ŸŒ
Python
peps.python.org โ€บ pep-0544
PEP 544 โ€“ Protocols: Structural subtyping (static duck typing) | peps.python.org
March 5, 2017 - isinstance() can be used with both data and non-data protocols, while issubclass() can be used only with non-data protocols. This restriction exists because some data attributes can be set on an instance in constructor and this information is not always available on the class object.
๐ŸŒ
GitHub
github.com โ€บ python โ€บ mypy โ€บ issues โ€บ 12970
Protocol can't constrain __init__ ยท Issue #12970 ยท python/mypy
June 11, 2022 - from typing import Protocol, Type class Proto(Protocol): def __init__(self, x: int) -> None: ... class Impl: def __init__(self) -> None: ...
Author ย  glyph
๐ŸŒ
Real Python
realpython.com โ€บ python-protocol
Python Protocols: Leveraging Structural Subtyping โ€“ Real Python
July 25, 2024 - In this tutorial, you'll learn about Python's protocols and how they can help you get the most out of using Python's type hint system and static type checkers.
๐ŸŒ
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 - So, why is this called a โ€œprotocolโ€ and what kinds of things is it good for? Letโ€™s go deeper to answer those questions. The built-in function len() works with any object that has a __len__() method. Objects donโ€™t have to declare that they have a __len__() method or subclass any special classes to work with len(). Python programmers call this state of affairs a protocol, and the most common example is probably the iteration protocol.
Find elsewhere
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 - Protocols in Python work a little bit differently. While the concept is the same as Clojure, Python does not need explicit protocol declaration.
Top answer
1 of 2
16

You can't instantiate a protocol class directly. This is currently implemented by replacing a protocol's __init__ with a method whose sole function is to enforce this restriction:

def _no_init(self, *args, **kwargs):
    if type(self)._is_protocol:
        raise TypeError('Protocols cannot be instantiated')

...

class Protocol(Generic, metaclass=_ProtocolMeta):
    ...

    def __init_subclass__(cls, *args, **kwargs):
        ...
        cls.__init__ = _no_init

Your __init__ doesn't execute because it isn't there any more.

This is pretty weird and messes with even more stuff than it looks like at first glance - for example, it interacts poorly with multiple inheritance, interrupting super().__init__ chains.

2 of 2
0

Summary

This has been fixed in later versions of Python (>=3.11) and in typing_extensions>=4.6.0.

Context

Per the comment

This issue in the Python bug tracker looks related: bugs.python.org/issue44807 ... So we may see some updates to Protocol/__init__ interaction in Python version 3.11.

Python 3.11 did address this problem

The __init__() method of Protocol subclasses is now preserved. (Contributed by Adrian Garcia Badarasco in gh-88970.)

For versions before 3.11, the typing_extensions>=4.6.0 package also has the fix.

Changed in version 4.6.0: Backported the ability to define __init__ methods on Protocol classes.

Example

Using import from typing_extensions==4.12.2

>>> from abc import ABC 
... from typing_extensions import Protocol  # DIFFERENT IMPORT
...  
... class AbstractBase(ABC): 
...     def __init__(self): 
...         print("AbstractBase.__init__ called") 
...  
... class Concrete1(AbstractBase): 
...     ... 
...  
... c1 = Concrete1()  # prints "AbstractBase.__init__ called" 
...  
... class ProtocolBase(Protocol): 
...     def __init__(self): 
...         print("ProtocolBase.__init__ called") 
...  
... class Concrete2(ProtocolBase): 
...     ... 
...  
... c2 = Concrete2()  # now prints "ProtocolBase.__init__ called"          
AbstractBase.__init__ called
ProtocolBase.__init__ called
๐ŸŒ
Python Tutorial
pythontutorial.net โ€บ home โ€บ python oop โ€บ python protocol
Python Protocol - Python Tutorial
March 31, 2025 - To make the calculate_total() more dynamic while leveraging type hints, you can use the Protocol from the typing module. The Protocol class has been available since Python 3.8, described in PEP 544.
๐ŸŒ
Python
typing.python.org โ€บ en โ€บ latest โ€บ reference โ€บ protocols.html
Protocols and structural subtyping โ€” typing documentation
Predefined protocol reference lists all protocols defined in typing and the signatures of the corresponding methods you need to define to implement each protocol.
๐ŸŒ
GitHub
github.com โ€บ python โ€บ mypy โ€บ issues โ€บ 10482
Inconsistent Treatment of Class Constructor by Protocol ยท Issue #10482 ยท python/mypy
May 16, 2021 - In effect we're describing a "factory". However, while MyPy is able to recognize incorrect protocol implementations when given a function, it fails to do the same for class constructors. Playground link: https://mypy-play.net/?mypy=latest&python=3.9&gist=3b9250b17032b659fc36746e659bcdb5 ยท
Published ย  May 16, 2021
Author ย  rmorshea
๐ŸŒ
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 :)

Capability ABC Protocols
Runtime checking 1 1 (with a decorator)
Static checking with mypy 1 1
Explicit interface (class Dog(Animal):) 1 1
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 interface 0 1
Number of code lines -1 (requires ABC inheritance and abstracmethod for every method) 0 (optionalProtocol inheritance)
Total score 1.5 4

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.

๐ŸŒ
Python.org
discuss.python.org โ€บ ideas
@typing.protocol decorator to create a class of type Protocol - Ideas - Discussions on Python.org
October 5, 2022 - Instead of having typing.Protocol, which we inherit to define a Protocol class, we could have a global @protocol decorator that declares a class as a Protocol. And the class that adheres to that Protocol should be decorated with a decorator having the name of the Protocol class it adheres to.
๐ŸŒ
DEV Community
dev.to โ€บ meseta โ€บ factories-abstract-base-classes-and-python-s-new-protocols-structural-subtyping-20bm
Python's new Protocols (Structural subtyping), Abstract Base Classes, and Factories - DEV Community
August 18, 2020 - Python has a built-in library for this called abc which stands for Abstract Base Class. The idea is to define an abstract base class for the file handler, against which new concrete implementations of different file handlers can be built.
๐ŸŒ
mypy
mypy.readthedocs.io โ€บ en โ€บ latest โ€บ protocols.html
Protocols and structural subtyping - mypy 1.20.0+dev.bdef6ef8734af07cab1bf0acadc1d8ab9add93c3 documentation
In Python 3.9 and later, the aliases in typing donโ€™t provide any extra functionality. You can define your own protocol class by inheriting the special Protocol class:
๐ŸŒ
Plain English
python.plainenglish.io โ€บ callable-protocol-in-python-eb2f5874f6ae
Callable Protocol in Python. Use objects as functions in Python. | by Sergio Daniel Cortez Chavez | Python in Plain English
January 14, 2022 - As you can see, Python defines many callable objects but you can create custom classes that return instances that are callable. The only requirement for creating a callable object is the implementation of the dunder method __call__ . For example, in the next example, we create an object that checks that a number is a positive integer value, this object can receive an optional max value as a constructor argument.
๐ŸŒ
Turingtaco
turingtaco.com โ€บ protocols-default-methods-inheritance-and-more
Protocols: Default Methods, Inheritance, and More
December 7, 2024 - Concrete classes TextFileHandler and SimpleDataProcessor (lines 32-41) provide specific implementations for the methods defined in the FileHandler and DataProcessor Protocols. Their instances are created and used in the read_process_write function (lines 43-45), illustrating how objects adhering to different Protocols can be handled within the same function. In this article, we explored the power of Python's Protocols.