Below is the complete source code of the class _ProtocolMeta
used as metaclass for typing.Protocol
in Python 3.9:
class _ProtocolMeta(ABCMeta):
# This metaclass is really unfortunate and exists only because of
# the lack of __instancehook__.
def __instancecheck__(cls, instance):
# We need this method for situations where attributes are
# assigned in __init__.
if ((not getattr(cls, '_is_protocol', False) or
_is_callable_members_only(cls)) and
issubclass(instance.__class__, cls)):
return True
if cls._is_protocol:
if all(hasattr(instance, attr) and
# All *methods* can be blocked by setting them to None.
(not callable(getattr(cls, attr, None)) or
getattr(instance, attr) is not None)
for attr in _get_protocol_attrs(cls)):
return True
return super().__instancecheck__(instance)
As indicated by the first comment, the only purpose of the class is to provide an __instancecheck__
hook for isinstance(instance, cls)
with cls
being a subclass of typing.Protocol
. Furthermore, in Protocol.__init_subclass__
, a class subclassing from Protocol
gets the attribute _is_protocol
set to True
if any of its bases is Protocol
, in other words, if the class is a Protocol in sense of PEP 544.
I understand that checking an object to be an instance of a Protocol requires a special treatmeant, since PEP 544 states:
A concrete type X is a subtype of protocol P if and only if X implements all protocol members of P with compatible types. In other words, subtyping with respect to a protocol is always structural.
But I cannot recognize this special treatment in the implementation given by the source code above. Moreover, I cannot figure out any reasonable rationale for the implementation above. Can somebody explain it, please?
Especially, I don't understand the condition getattr(instance, attr) is not None
in the line 1110:
Why it is needed to check if a protocol attribute (assured to exist on the instance: hasattr(instance, attr)
) is not None
on the instance if this attribute is callable in the protocol? If to check for a value, then I would think of checking for being callable, not just for just None
.