The methods __subclasscheck__
and __subclasshook__
are used to determine if a class is regarded as subclass of another. However, their documentation is very limited, even in advanced Python books. How are they meant to be used and what is their difference (higher priority, side of relationship they refer to etc...)?
It returns True when a class is found to be subclass of a ABC class, it returns False if it is not and returns NotImplemented if the subclass check is continued with the usual mechanism. This method is defined in the ABC class with some conditions.
Understanding Python super() with __init__() methods It is known as a constructor in Object-Oriented terminology. This method when called, allows the class to initialize the attributes of the class. The super() function allows us to avoid using the base class name explicitly.
Python issubclass() is built-in function used to check if a class is a subclass of another class or not. This function returns True if the given class is the subclass of given class else it returns False . Return Type: True if object is subclass of a class, or any element of the tuple, otherwise False.
The __new__() is a static method of the object class. It has the following signature: object.__new__(class, *args, **kwargs) Code language: Python (python) The first argument of the __new__ method is the class of the new object that you want to create.
Both methods can be used to customize the result of the issubclass()
built-in function.
__subclasscheck__
class.__subclasscheck__(self, subclass)
Return true if subclass should be considered a (direct or indirect) subclass of class. If defined, called to implement
issubclass(subclass, class)
.Note that these methods are looked up on the type (metaclass) of a class. They cannot be defined as class methods in the actual class. This is consistent with the lookup of special methods that are called on instances, only in this case the instance is itself a class.
This method is the special method that is responsible for the customization of the issubclass
check. Like the "Note" states it has to implemented on the metaclass!
class YouWontFindSubclasses(type): def __subclasscheck__(cls, subclass): print(cls, subclass) return False class MyCls(metaclass=YouWontFindSubclasses): pass class MySubCls(MyCls): pass
This implementation will return False even if you have genuine subclasses:
>>> issubclass(MySubCls, MyCls) <class '__main__.MyCls'> <class '__main__.MySubCls'> False
There are actually more interesting uses for __subclasscheck__
implementations. For example:
class SpecialSubs(type): def __subclasscheck__(cls, subclass): required_attrs = getattr(cls, '_required_attrs', []) for attr in required_attrs: if any(attr in sub.__dict__ for sub in subclass.__mro__): continue return False return True class MyCls(metaclass=SpecialSubs): _required_attrs = ['__len__', '__iter__']
With this implementation any class that defines __len__
and __iter__
will return True
in a issubclass
check:
>>> issubclass(int, MyCls) # ints have no __len__ or __iter__ False >>> issubclass(list, MyCls) # but lists and dicts have True >>> issubclass(dict, MyCls) True
In these examples I haven't called the superclasses __subclasscheck__
and thereby disabled the normal issubclass
behavior (which is implemented by type.__subclasscheck__
). But it's important to know that you can also choose to just extend the normal behavior instead of completely overriding it:
class Meta(type): def __subclasscheck__(cls, subclass): """Just modify the behavior for classes that aren't genuine subclasses.""" if super().__subclasscheck__(subclass): return True else: # Not a normal subclass, implement some customization here.
__subclasshook__
__subclasshook__(subclass)
(Must be defined as a class method.)
Check whether subclass is considered a subclass of this ABC. This means that you can customize the behavior of
issubclass
further without the need to callregister()
on every class you want to consider a subclass of the ABC. (This class method is called from the__subclasscheck__()
method of the ABC.)This method should return
True
,False
orNotImplemented
. If it returnsTrue
, the subclass is considered a subclass of this ABC. If it returnsFalse
, the subclass is not considered a subclass of this ABC, even if it would normally be one. If it returnsNotImplemented
, the subclass check is continued with the usual mechanism.
The important bit here is that it's defined as classmethod
on the class and it's called by abc.ABC.__subclasscheck__
. So you can only use it if you're dealing with classes that have an ABCMeta
metaclass:
import abc class MyClsABC(abc.ABC): @classmethod def __subclasshook__(cls, subclass): print('in subclasshook') return True class MyClsNoABC(object): @classmethod def __subclasshook__(cls, subclass): print('in subclasshook') return True
This will only go into the __subclasshook__
of the first:
>>> issubclass(int, MyClsABC) in subclasshook True >>> issubclass(int, MyClsNoABC) False
Note that subsequent issubclass
calls don't go into the __subclasshook__
anymore because ABCMeta
caches the result:
>>> issubclass(int, MyClsABC) True
Note that you generally check if the first argument is the class itself. That's to avoid that subclasses "inherit" the __subclasshook__
instead of using normal subclass-determination.
For example (from the CPython collections.abc
module):
from abc import ABCMeta, abstractmethod def _check_methods(C, *methods): mro = C.__mro__ for method in methods: for B in mro: if method in B.__dict__: if B.__dict__[method] is None: return NotImplemented break else: return NotImplemented return True class Hashable(metaclass=ABCMeta): __slots__ = () @abstractmethod def __hash__(self): return 0 @classmethod def __subclasshook__(cls, C): if cls is Hashable: return _check_methods(C, "__hash__") return NotImplemented
So if you check if something is a subclass of Hashable
it will use the custom __subclasshook__
implementation that is guarded by the if cls is Hashable
. However if you have an actual class implementing the Hashable
interface you don't want it to inherit the __subclasshook__
mechanism but you want the normal subclass mechanism.
For example:
class MyHashable(Hashable): def __hash__(self): return 10 >>> issubclass(int, MyHashable) False
Even though int
implements __hash__
and the __subclasshook__
checks for an __hash__
implementation the result in this case is False
. It still enters the __subclasshook__
of Hashable
but it immediately returns NotImplemented
which signals to ABCMeta
that it should proceed using the normal implementation. If it didn't have the if cls is Hashable
then issubclass(int, MyHashable)
would return True
!
__subclasscheck__
and when __subclasshook__
?It really depends. __subclasshook__
can be implemented on the class instead of the metaclass, but requires that you use ABCMeta
(or a subclass of ABCMeta
) as metaclass because the __subclasshook__
method is actually just a convention introduced by Pythons abc
module.
You can always use __subclasscheck__
but it has to be implemented on the metaclass.
In practice you use __subclasshook__
if you implement interfaces (because these normally use abc
) and want to customize the subclass mechanism. And you use __subclasscheck__
if you want to invent your own conventions (like abc
did). So in 99.99% of the normal (not fun) cases you only need __subclasshook__
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With