Python Custom isinstance() and issubclass() Checks
Learn how Python customizes isinstance() and issubclass() through __instancecheck__() and __subclasscheck__() on a metaclass, including virtual relationships and preserving normal inheritance checks.
Advanced
Python's
isinstance() and
issubclass() normally answer whether an object or class belongs to a type hierarchy. Python also lets a class customize those questions through methods on its metaclass. This lesson focuses on that advanced but useful Core Python data-model protocol.
Customize isinstance() with __instancecheck__()
When Python evaluates
isinstance(value, SomeClass), it can call
__instancecheck__() on the metaclass of
SomeClass. The method receives the candidate object and returns the custom answer.
|
|
EvenNumber is not a base class of int. The metaclass decides that even integers count as instances for this check.
Customize issubclass() with __subclasscheck__()
Similarly,
issubclass(candidate, SomeClass) can be customized by
__subclasscheck__() on the metaclass of
SomeClass. The candidate is a class, and the hook decides whether it should be considered a subclass.
|
|
Job does not inherit from Runnable. It is accepted because the custom subclass check recognizes its run() method.
The hooks belong on the metaclass
A crucial detail is where Python looks for these methods.
__instancecheck__() and
__subclasscheck__() are looked up on the metaclass of the class being checked. Defining one directly on the ordinary class does not create the same customization point.
|
|
Marker defines a method with the same name, but it is an ordinary instance method of Marker, not the metaclass hook used by isinstance().
Preserve normal inheritance checks when useful
A custom hook can extend the normal type behavior instead of replacing it. The metaclass can delegate to
super() and then add its own rule. This is useful when real subclasses should keep behaving normally.
|
|
The normal inheritance check remains true for Child, while an unrelated object with the right marker is also accepted.
Subclass checks can use the same idea
The same delegation pattern works for
__subclasscheck__(). Keeping the built-in inheritance answer and adding a protocol rule can make a custom type boundary easier to express.
|
|
Employee passes through ordinary inheritance; Contractor is accepted by the additional protocol rule.
Understand virtual relationships
A custom check can describe a relationship without changing
__bases__ or
__mro__. The classes involved are still ordinary classes with their real inheritance structures; the custom result only changes how these built-in checks answer a question.
|
|
The checks report a virtual relationship, but User still has its ordinary class bases. No inheritance was added.
Do not confuse these hooks with ordinary class methods
These hooks are not ordinary class methods such as those taught earlier. Python looks them up on the class's type—the metaclass—because the second argument to
isinstance() and the first argument to
issubclass() is itself a class.
|
|
MappingLike uses its metaclass to customize the meaning of isinstance(..., MappingLike).
Use these hooks sparingly
Customizing instance and subclass checks can model protocol-style relationships, but it also makes type questions less obvious. Keep the rule small, document the relationship, and prefer ordinary inheritance when the relationship is truly structural in the class hierarchy.
The standard library also uses these mechanisms. For example,
ABCMeta supports virtual subclasses and subclass hooks. Those facilities are a separate Standard Library topic and are not expanded here.
Key takeaways
- isinstance(obj, Class) can be customized through __instancecheck__() on Class's metaclass.
- issubclass(Subclass, Class) can be customized through __subclasscheck__() on Class's metaclass.
- The customization methods belong on the metaclass, not as ordinary methods on the checked class.
- super().__instancecheck__() and super().__subclasscheck__() can preserve normal inheritance behavior while adding a rule.
- Custom checks can describe virtual relationships without changing __bases__ or __mro__.
Thanks for your feedback.
Mark this learning resource complete to track your learning progress.