__subclasscheck__ and __subclasshook__ in Python


Python, a generally adaptable and effective programming language, has picked up significant popularity over a long time. The item-oriented nature of Python lets within the execution of a few high-quality capacities, such as inheritance and polymorphism. In this post, we will delve into two lesser-recognized, yet charming techniques that allow for custom-designed inheritance checks in Python: subclasscheck and subclasshook.

What are Subclasscheck and Subclasshook?

In Python, it is not unusual to determine if a class is a subclass of some other class by utilizing the built-in issubclass() function. By default, this function checks the inheritance tree to decide the connection between lessons. However, Python also provides a manner to override this default behavior using the unique methods subclasscheck and subclasshook.

  • __subclasscheck__(cls)  This technique is called by using the issubclass() function to test if a category is a subclass of every other class. By default, it returns the result of the usual inheritance test, but it is able to be overridden to alternate this behavior.

  • __subclasshook__(cls)  This method can be defined in an abstract base class (ABC) to customize the subclass check performed by issubclass(). It is called by the default implementation of subclasscheck in the ABC.

The Subclasshook Method

To have a clear understanding of the working of the subclasshook method, let's go through an example. Suppose we've got an abstract base class referred to as 'Shape' with two required methods: 'area' and 'perimeter'. Any class that desires to be considered a subclass of 'Shape' must put into effect those methods.

Step 1  Determine an abstract base Class 'Shape' with two specific methods: 'area' and 'perimeter'.

Step 2  Generate a custom class 'Circle' that implements the specified methods 'area' and 'perimeter'.

Step 3 − Override the subclasshook method in the 'Shape' class to specify custom criteria for determining if a class is a subclass. In this situation, the standards are that the class ought to have the 'area' and 'perimeter' methods.

Step 4 − Use the issubclass() function to test if 'Circle' is a subclass of 'Shape'. With the custom subclasshook method, the result is 'True' because 'Circle' meets the custom criteria.

Example

Now, let's create a custom class 'Circle' that implements these methods −

from abc import ABCMeta, abstractmethod

class Shape(metaclass=ABCMeta):
   @abstractmethod
   def area(self):
      pass
   
   @abstractmethod
   def perimeter(self):
      pass
class Circle:
   def __init__(self, radius):
      self.radius = radius
   
   def area(self):
      return 3.14 * self.radius * self.radius
   
   def perimeter(self):
      return 2 * 3.14 * self.radius
print(issubclass(Circle, Shape))

Even though the 'Circle' class implements the required methods, the issubclass() function will still return 'False' when checking if 'Circle' is a subclass of 'Shape' −

Output

False

This is where the subclasshook method comes into play. We can override this method in the 'Shape' class to specify our custom criteria for determining if a class is a subclass −

Example

class Shape(metaclass=ABCMeta):
   @abstractmethod
   def area(self):
      pass
   
   @abstractmethod
   def perimeter(self):
      pass
   
   @classmethod
   def __subclasshook__(cls, other):
      if cls is Shape:
         if all(hasattr(other, method) for method in ['area', 'perimeter']):
            return True
      return NotImplemented
print(issubclass(Circle, Shape))

Output

Here is the output, if we check if 'Circle' is a subclass of 'Shape'.

True

The Subclasscheck Method

In some cases, you might want to override the subclasscheck method itself, rather than using subclasshook. This can offer extra first-class-grained control over the inheritance test. Here is an example 

Step 1  Determine a custom base class 'CustomBase' that overrides the subclasscheck approach. Instead of testing for a general inheritance connection, we test if the subclass has a callable 'magic_attribute' approach.

Step 2  Generate two classes, 'DerivedWithMagic' and 'DerivedWithoutMagic'. The former has the 'magic_attribute' method, while the latter does not.

Step 3  Utilize the issubclass() function to test in case 'DerivedWithMagic' and 'DerivedWithoutMagic' are subclasses of 'CustomBase'. The conclusion is 'True' for 'DerivedWithMagic' since it has the desired 'magic_attribute' method, and 'False' for 'DerivedWithoutMagic' because it no longer has the specified method.

Example

class CustomBase:
   def __subclasscheck__(self, subclass):
      return (hasattr(sub

class, "magic_attribute") and
callable(getattr(subclass, "magic_attribute")))
class DerivedWithMagic:
def magic_attribute(self):
pass
class DerivedWithoutMagic:
pass
print(issubclass(DerivedWithMagic, CustomBase))
print(issubclass(DerivedWithoutMagic, CustomBase))

Output

Here is the output, if we check if 'Circle' is a subclass of 'Shape'.

True
False

Practical Use Cases

While the default inheritance mechanism in Python is suitable for most scenarios, there are cases where customizing subclass checks using __subclasscheck__ and __subclasshook__ can be beneficial −

  • **Protocol enforcement**  By using these methods, you can enforce certain protocols that subclasses must adhere to. In our preceding instance, we decided that any class treated as a subclass of 'Shape' has to perform the 'area' and 'perimeter' methods.

  • **Mixin lessons**  Mixin classes are created to give particular behaviors to other classes, but they are not meant to be used as standalone classes. You can use __subclasscheck__ or __subclasshook__ to define custom inheritance policies that identify classes by utilizing the mixin as subclasses, despite the fact that they do not inherit without delay from it.

  • **Loose coupling**  In some situations, it's beneficial to minimize dependencies between components in a software system. By using __subclasscheck__ and __subclasshook__, you can establish relationships between classes without having to create a rigid inheritance hierarchy.

Conclusion

The __subclasscheck__ and __subclasshook__ methods in Python offer a powerful way to customize inheritance checks. These methods can be especially helpful when you want to enforce specific requirements for subclass relationships or provide more flexible inheritance structures. By understanding and leveraging these special methods, you can create more adaptable and robust Python programs.

Updated on: 09-May-2023

402 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements