3

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.

Min-Soo Pipefeet
  • 2,208
  • 4
  • 12
  • 31

0 Answers0