Instance checking with data Protocols?

Hi! I’m having a little trouble understanding what I can do with the new runtime checkable protocols. The PEP says that data protocols (those with typed attributes) can be checked with isinstance, so I’ve tried this:

from typing import Protocol, runtime_checkable

@runtime_checkable
class Foo(Protocol):
    x: int

class Bar(object):
    def __init__(self, x):
        self.x = x

assert isinstance(Bar(10), Foo)
assert not isinstance(Bar(10.), Foo)  # This fails, which I didn't expect

I feel like I’m missing something about how protocols are supposed to be used. Is there a way for me to get the result I expected? I.e. this instance has attribute x which is an int?

Thanks for any help!

1 Like

My understanding is runtime_checkable only checks the attributes of the class, not recursively check all the attributes’ types. It’s probably not be very viable to implement with isinstance.

1 Like

Oh, so it only checks that they exist, not what type they are? That’s good to know, and is not clear to me from the PEP or docs. In particular, this specification makes it sound like it would check the type of attribute since that information is available at runtime.

If I wanted this behavior, would it be terribly fraught for me to create a new Protocol class with an __instance_check__ more like this?

def __instance_check__(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
                (  # My modification:
                    hasattr(instance, attr)
                    and isinstance(getattr(instance, attr), attrtyp)
                )
                for attr, attrtyp in get_type_hints(cls).items()):
            return True
    return super().__instancecheck__(instance)

I don’t think so, although I’d personally prefer using polymorphism + static type checking for this, i.e. create two Bar subclasses that accept either int or float.

The main idea here is I want to check if a class satisfies a protocol at runtime without being the one to implements that class, or even knowing it was implemented before. I also want there to be one way to do this, instead of the normal mess of attribute and value checking.

I just want to be able to say isinstance(ar, Array[int]), and was hoping Protocols could at least get me to isinstance(ar, IntArray).