6 Things to Know About Subclassing in Python — Why and How | by Yong Cui | Apr, 2022

Touching base on some core options in Python as an OOP language

Picture by Robert Collins on Unsplash

At its core, Python is an OOP language by design. It has all the pieces applied as objects, packages, modules, lessons, capabilities, and definitely occasion objects of lessons. Once we construct our purposes, we often should create customized lessons to bundle associated information and functionalities collectively — a typical OOP strategy to addressing programming issues.

With the venture’s development in its scope, it’s virtually inevitable that you must create subclasses. On this article, I’d like to focus on some options/concerns that it is best to word once you outline your subclass. Please word that I don’t use any assumptions concerning your data stage, so I’ll cowl very fundamental stuff, however these key factors ought to be adequate to get you began with subclassing.

With out additional ado, let’s get it began.

Very first thing first, why can we trouble making a subclass? Once we resolve to create a subclass, we must always notice that it’s not only one subclass we’re going to create — if that’s the case, we must always’ve not created a subclass, to start with, as we are able to simply merely work on the preliminary class. In different phrases, when we have to create subclasses, we must always have the necessity of making at the very least two subclasses that share the identical superclass. Should you don’t discover that, I don’t suppose it is best to subclass.

The above paragraph appears to speak in regards to the “aftermath” — issues that you simply examine after you’ve already created a subclass. If we step again, we ought to be questioning when to create subclasses.

Suppose that we’re engaged on a venture to create an utility for folks to make use of in a college. We all know that there are two classes of individuals in a college: college students and academics. So we create two lessons that handle the info for every class. Some naked buildings of those two lessons are proven beneath.

Pupil and Instructor (Separate Lessons)

Within the Pupil class, we outline three attributes: identify, student_id, and grade and three occasion strategies: register_course, load_account, and check_in. Within the Instructor class, we additionally outline three attributes: identify, staff_id, and course_covered, and three occasion strategies: request_vacation, load_account, and check_in. As you may inform, these two lessons share comparable attributes and strategies, and the next determine supplies a visible abstract.

Relationship Between Pupil and Instructor

Within the determine above, we word how these two lessons are alike, for probably the most half, and this similarity between a number of lessons is the idea for making a superclass.

Please word that in actuality, your lessons could be extra difficult than what’s proven within the determine. However crucial factor to look at is that when two or extra lessons share overlapping functionalities, it is best to think about making a superclass, which in flip handles these shared functionalities.

Crucial profit from making a subclass is that we make our code extra structured — cleaner and extra maintainable. That’s, as an alternative of implementing every performance in separate lessons, the shared implementations go to the superclass, making it simpler to take care of.

We now know the rationale for making a superclass. The plain first query is what we must always identify our superclass. Most often, we are able to merely take the angle from the nominal or semantic perspective. As each college students and academics belong to the bigger idea of “individuals”, we are able to simply identify the superclass as Particular person.

Within the Particular person class, we anticipate that the superclass ought to seize the shared functionalities for the subclasses: Pupil and Instructor. Thus, to assist us outline the construction of the superclass, it’ll be useful when you can create a diagram displaying you the important skeleton of the superclass, in addition to that of the subclasses with out specifying any implementation particulars for now.

Construction of Superclass — Subclass

As you may see, we place the shared attributes: identify and person_id (combining student_id and staff_id) and shared strategies: load_account and check_in within the superclass. Apparently, the distinct attributes and strategies are anticipated to be applied within the subclasses: Pupil and Instructor.

Within the earlier part, we’ve a plan concerning the relative preparations of the shared and distinct functionalities between superclass and subclasses. Now, it’s time to begin to write the code for the superclass. Let’s see the code beneath earlier than I begin the reason.

Superclass — Particular person

Within the superclass’s initialization technique, we solely embrace the 2 attributes which can be shared by Pupil and Instructor: identify and person_id. Within the meantime, we solely outline strategies which can be shared by the subclasses: load_account and check_in.

Thus, the important thing for outlining the superclass is to outline the attributes and strategies that the subclasses ought to inherit.

After we format the important implementation of the superclass Particular person, it’s now to step again to refactor our authentic Pupil and Instructor lessons, as they now ought to inherit from the Particular person class as deliberate.

Defining a subclass

With an current superclass, defining a superclass includes the next syntax: class Subclass(Superclass). Making use of this way, we are able to have the next heads for the Pupil and Instructor lessons.

class Pupil(Particular person):
cross
class Instructor(Particular person):
cross

Inheriting the attributes and strategies

As of now, we don’t have any code within the physique of Pupil and Instructor. However the subclassing permits the subclass to inherit all of the attributes and strategies that we’ve outlined within the superclass. That’s precisely the key benefit of subclassing — the shared functionalities are immediately out there to a superclass’s subclasses with out the subclassing doing something. Let’s observe this impact:

scholar = Pupil("David", 20020)
scholar.load_account(50)
trainer = Instructor("Ashley", 1000033)
trainer.load_account(100)

As you may see, we are able to create an occasion of the Pupil class utilizing the constructor that’s outlined within the superclass and name the load_account technique on this occasion. We will do the identical issues with the Instructor class.

Overriding the initialization technique

In lots of instances, we have to override the initialization technique within the subclass, as a result of we could have extra attributes than what’s outlined within the superclass. Let’s see the code first, solely utilizing the Pupil class for example, and you’ll apply the identical methods to the Instructor class.

class Pupil(Particular person):
def __init__(self, identify, student_id, grade):
tremendous().__init__(identify, student_id)
self.grade = grade
scholar = Pupil("David", 20020, 3)
print(scholar.__dict__)
# output: 'identify': 'David', 'person_id': 20020, 'grade': 3

Within the __init__ technique, we are able to initialize the grade attribute as we usually do in a customized class. Apart from that, probably the most vital factor to look at is that we use the superclass’s initialization technique by calling tremendous().__init__(identify, student_id). Calling tremendous() creates a proxy object that refers back to the superclass (Particular person) such that we are able to use the superclass’s initialization technique by sending identify and person_id. You could know that, however simply as a pleasant reminder that calling __init__ doesn’t get hold of any return worth and it’s merely setting the initialization attributes.

Overriding different strategies

When you must override different strategies, it’s not very totally different from overriding __init__. If you must override a way utterly, you may simply write the physique of the strategy with out calling the superclass’s technique by utilizing tremendous(). You possibly can outline the strategy as if there have been no implementations for a similar technique within the superclass, due to the technique decision order (MRO), as mentioned subsequent.

Should you override the strategy partially, you should use tremendous() to re-use the identical implementation because the superclass, as proven beneath.

Up to date Pupil class

Within the up to date Pupil class, we now implement the check_in technique. Notably, we use tremendous() to create a proxy object that’s an occasion object of the superclass, such that we are able to name the occasion technique of the superclass on this proxy occasion object. Thus, tremendous() is exclusive within the sense that it may well’t solely create a proxy object to the superclass but in addition is usually a proxy object to the superclass’s occasion, supplying you with entry to the occasion strategies.

When you’ve gotten a category hierarchy (superclass-subclass), once you name a way on an occasion of the subclass, you could need to know the way the strategy is known as. For instance, your subclass could implement strategies that the superclass doesn’t have or implement strategies that the superclass implements too.

How can we all know which implementation is used? That is the place the idea of MRO involves play — it decided how the strategy is resolved following a selected order. The next determine exhibits how the MRO works.

The MRO Stream

As you may see, once you name a way, say some_method, Python first tries to resolve it by finding it within the Pupil class. If applied, this implementation is used. If not, it tries to go to the Pupil’s superclass Particular person, if applied, it’s resolved. In any other case, it’ll go as much as the article class, the place all customized lessons inherit from by default. If nonetheless not resolved, elevate the AttributeError.

You possibly can really examine a category’s MRO by calling the mro technique on the category, which supplies you the record of lessons within the order of how a way is resolved.

>>> Pupil.mro()
[<class '__main__.Student'>, <class '__main__.Person'>, <class 'object'>]

As you may anticipate, the order matches that proven within the above determine, for the Pupil class, it’s Pupil -> Particular person -> object.

When you’ve gotten a number of inheritances, you’ll find out the MRO as effectively by calling the mro technique. Please word that it’s typically not a good suggestion to have a number of inheritances, which may complicate your codebase. In different phrases, it’s most popular to make use of a single inheritance.

One solution to encapsulate information in OOP is to create protected and personal strategies. Though Python doesn’t actually use the idea/key phrases of public vs. personal strategies, we are able to create protected and personal strategies by prefixing the strategy identify with one or two underscores, as proven beneath.

class Particular person:
def __init__(self, identify, person_id):
self.identify = identify
self.person_id = person_id
def _login(self):
cross
def __logout(self):
cross

Within the above code snippet, _login is a protected technique whereas __logout is a non-public technique. Each strategies are personal, which means that they’re not meant to be known as by the customers exterior the category, and you’ll word this once you use an IDE (the determine utilizing PyCharm) the place out there strategies are prompted via auto-completion hints, as proven beneath:

No Hints for Private Strategies

The distinction between protected and personal strategies is just not solely about utilizing one or two underscores, and the opposite essential distinction is their accessibility inside the class’s subclasses. You possibly can name the superclass’s protected strategies, however not the non-public strategies. You possibly can observe this impact beneath.

class Pupil(Particular person):
def test_login_logout(self):
self._login()
self.__logout()
scholar = Pupil("David", 20020)
scholar.test_login_logout()
# ERROR:
AttributeError: 'Pupil' object has no attribute '_Student__logout'

From the printout, we are able to see that _login is invoked with none points. Nevertheless, __logout can’t be invoked, elevating the AttributeError. If we take a better look, we word that it’s not making an attempt to name __logout, however making an attempt to name _Student__logout as an alternative. This appears unusual, nevertheless it’s often called the identify mangling characteristic. That’s, once you outline a non-public technique, say it’s named __private_method in a category named Knowledge, this technique is known as as _Data__private_method as an alternative.

Due to their totally different accessibility inside subclasses, it is best to outline protected strategies when you anticipate them for use within the subclasses, and when you don’t need the subclasses to make use of them, it is best to outline non-public strategies as an alternative.

On this article, we reviewed the important thing factors about defining subclasses in Python. In essence, in a category hierarchy, you’ve gotten a superclass, during which you outline the shared attributes and strategies.

You must have a number of subclasses that inherit from this superclass — when you’re noticing that you simply’re solely utilizing one single subclass, it may well’t be proper — there isn’t any must create a superclass to begin with.

More Posts