22

The new typing module contains several objects with names like "SupportsInt" (-Float, -Bytes, etc.). The name, and the descriptions on the documentation page for the module, might be read to suggest that you can test whether an object is of a type that "supports __int__()". But if you try to use isinstance(), it gives a response that makes it clear that that isn't something you are meant to do:

>>> isinstance(5, typing.SupportsInt)
(Traceback omitted)
TypeError: Protocols cannot be used with isinstance().

On the other hand, you can use issubclass():

>>> issubclass((5).__class__, typing.SupportsInt)
True
>>> issubclass(type(5), typing.SupportsInt)
True

What is a "protocol" in this context? Why does it disallow the use of isinstance() in this way?

Hammerite
  • 21,755
  • 6
  • 70
  • 91
  • 2
    The purpose of the `typing` module is **not** to provide a way to perform those checks. It's meant to provide a common way to provide the types of functions etc. in their annotations. Duck-typing is still preferred, you can use those types to tell your user how the function is intended to be called. – Bakuriu Jan 18 '16 at 12:14
  • @Bakuriu, but the typing module is provided in part to allow static type-checking, and so it is reasonable to suppose that it will provide the machinery for identifying (say) whether a particular object is of a particular type. If I invoke a function passing 5 as a parameter that is annotated as being of type typing.SupportsInt, then how will a static type checker know that this is valid? – Hammerite Jan 19 '16 at 00:05
  • Not to mention that runtime checking is essential if we're trying to dynamically load something from details in a configuration file, and we need to check that what we loaded does in fact conform to the expected protocol. – Alex Peters Apr 17 '22 at 00:26

4 Answers4

7

If you reached this question wanting to get around the error (like I did), the solution is to decorate the protocol class with @runtime_checkable:

from typing import Protocol, runtime_checkable

@runtime_checkable
class StorageProtocol(Protocol):
    ...

@typing.runtime_checkable

Mark a protocol class as a runtime protocol.

Such a protocol can be used with isinstance() and issubclass().

storage = ...  # get something that must implement StorageProtocol
if not isinstance(storage, StorageProtocol):
    raise RuntimeError(...)
Alex Peters
  • 2,601
  • 1
  • 22
  • 29
0

As the documentation says: At runtime, isinstance(x, T) will raise TypeError. In general, isinstance() and issubclass() should not be used with types. ( https://docs.python.org/3/library/typing.html?highlight=typing#typing.TypeVar )

Duncan MC Leod
  • 113
  • 1
  • 8
  • 7
    This is a description of what happens. The question asks why this is the behaviour. You have answered a different question to the one that was asked. – Hammerite Dec 08 '16 at 10:47
0

This is all of the reasoning given in PEP 484, the PEP for the typing module:

Because typing.Callable does double-duty as a replacement for collections.abc.Callable , isinstance(x, typing.Callable) is implemented by deferring to `isinstance(x, collections.abc.Callable) . However, isinstance(x, typing.Callable[...]) is not supported.

A protocol is also known as a magic method. These are most of the python protocols (full list here):

>>> dir(object)
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', 
'__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', 
'__setattr__', '__sizeof__', '__str__', '__subclasshook__']

I have not found any clear reason for why typing does not support isinstance. The reason that issubclass works is that isinstance uses the __class_ protocol which is not allowed in typing, while issubclass uses the __subclasshook__ protocol which is allowed. I believe the reason is that the functionality was already coded in collections.abc.Callable and they did not want to recode it in the typing module.

rassar
  • 5,412
  • 3
  • 25
  • 41
0

Protocols are intended to be used together with static type checking, to illustrate that a method can use anything that satisfies the protocol, so that duck typing can be statically checked.

It intentionally does not add any kind of runtime behaviour. Using protocols should strictly decrease the amount of implicit branching in your code instead of increasing it. You can inherit from a protocol, but that's just to get warnings if you don't implement methods.

saolof
  • 1,097
  • 1
  • 15
  • 12