最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - I can't figure out why `isinstance()` returns `True` for these subclasses - Stack Overflow

programmeradmin1浏览0评论

I'm using the Python package ariadne, v0.23.0.

I wrote a utility to scan my code for instances of ariadne.types.SchemaBindable, but it's also unintentionally picking up the SchemaBindable subclasses that I've imported:

ariadne.input.InputType( SchemaBindable )
ariadne.objects.ObjectType( SchemaBindable )
ariadne.scalars.ScalarType( SchemaBindable )
ariadne.unions.UnionType( SchemaBindable )

I ran a test in a Python shell, and sure enough, isinstance() is returning True when comparing those classes to SchemaBindable:

isinstance( ObjectType, SchemaBindable )  ->  True
...etc...

SchemaBindable even appears to be an instance of itself:

isinstance( SchemaBindable, SchemaBindable )  ->  True

Meanwhile, issubclass() continues to also return True:

issubclass( ObjectType, SchemaBindable )  ->  True

Make it make sense.

I'm using the Python package ariadne, v0.23.0.

I wrote a utility to scan my code for instances of ariadne.types.SchemaBindable, but it's also unintentionally picking up the SchemaBindable subclasses that I've imported:

ariadne.input.InputType( SchemaBindable )
ariadne.objects.ObjectType( SchemaBindable )
ariadne.scalars.ScalarType( SchemaBindable )
ariadne.unions.UnionType( SchemaBindable )

I ran a test in a Python shell, and sure enough, isinstance() is returning True when comparing those classes to SchemaBindable:

isinstance( ObjectType, SchemaBindable )  ->  True
...etc...

SchemaBindable even appears to be an instance of itself:

isinstance( SchemaBindable, SchemaBindable )  ->  True

Meanwhile, issubclass() continues to also return True:

issubclass( ObjectType, SchemaBindable )  ->  True

Make it make sense.

Share Improve this question asked Feb 5 at 23:57 odigityodigity 8,1766 gold badges41 silver badges58 bronze badges 2
  • See stackoverflow.com/questions/67355992/… – PM 77-1 Commented Feb 6 at 0:02
  • @PM77-1 That doesn't address my question. – odigity Commented Feb 6 at 0:29
Add a comment  | 

3 Answers 3

Reset to default 1

ariadne.types.SchemaBindable is documented as a regular class that you're supposed to extend to create bindables, but it's actually implemented as a protocol, and marked runtime-checkable:

@runtime_checkable
class SchemaBindable(Protocol):
    # docstring omitted because it's long

    def bind_to_schema(self, schema: GraphQLSchema) -> None:
        """Binds this `SchemaBindable` instance to the instance of GraphQL schema."""

Runtime-checkable protocols use a metaclass __instancecheck__ method to customize isinstance checks. Any object that has the attributes specified by the protocol will be considered an instance of the protocol.

But if you write a subclass of SchemaBindable:

class YourBindable(SchemaBindable):
    def bind_to_schema(self, schema):
        # implementation here

then that subclass has a bind_to_schema attribute, and the checker logic doesn't care that that attribute is meant to be a method implementation for instances of YourBindable. It just checks that the attribute exists, and if the attribute is supposed to be callable, it checks that the attribute isn't None:

        for attr in cls.__protocol_attrs__:
            try:
                val = getattr_static(instance, attr)
            except AttributeError:
                break
            # this attribute is set by @runtime_checkable:
            if val is None and attr not in cls.__non_callable_proto_members__:
                break
        else:
            return True

        return False

So that means that subclasses of SchemaBindable get treated as instances of SchemaBindable, even though they probably shouldn't be.

so, what is exactly your problem?

isinstance just won't lie to you: Ariadne is a special library which not only have a class hierarchy,where those other classes do inherit from SchemaBindable, but in order to do its thing, it also needs that the SchemaBindable class counts as an instance of itself. That is possible in Python, for example, by customizing the __subclasscheck__ method .

And then it just happens that everything you list is, in fact, an instance of SchemaBindable. Maybe you need to add some othe way, in your test, to know if what you are looking at is a class you wrote - there are several ways to do that, but since you didn't include examples of your code (not from the actual classes you want to test and neither from your utility), there is little we can help further with.

This behavior occurs because SchemaBindable in Ariadne is likely implemented as a metaclass, not a regular class. Metaclasses are classes for classes - they define how classes behave.

When a class uses a metaclass:

  • The class itself becomes an instance of the metaclass
  • The class also remains a subclass of the metaclass

This explains why both isinstance() and issubclass() return True, and why SchemaBindable appears to be an instance of itself. To fix your scanning utility, you need to use issubclass() exclusively and add a check to filter out SchemaBindable itself.

Here's how to modify your scanning to only get subclasses, excluding SchemaBindable itself:

Instead of isinstance() check, use this:

if issubclass(type_, SchemaBindable) and type_ != SchemaBindable:
    # Found a subclass that isn't SchemaBindable itself
    pass

This will only match ObjectType, InputType, ScalarType, UnionType, etc., but not SchemaBindable itself.

发布评论

评论列表(0)

  1. 暂无评论