This article is part of an ongoing series on Object Oriented Programming (OOP) in Python. In this article I will start the discussion from where we left in my last post - OOP in Python - Understanding a Class.
In the last post, we learned about the building blocks of a Class and constructed our first Subclass. In this post, I will start off by explaining the construction of the subclass - NumListExt01
which I have renamed here as Customer01
for ease of discussion. Then I will walk a bit backward and explain the concept of inheritance in a bit more detail, and the will again come back to the subclass and expand on it with some additional concepts.
Subclass¶
Recap¶
We will start with the class and subclass examples that we created in our last post. For ease of explanation, I have renamed the subclass from NumListExt01
to Customer01
.
# super class - NumList
class NumList:
def __init__(self):
self.__list = []
def add_value(self, val):
self.__list.append(val)
def remove_value(self):
rv = self.__list[-1]
del self.__list[-1]
return rv
def get_list(self):
return self.__list
# subclass - Customer01
class Customer01(NumList):
def __init__(self):
NumList.__init__(self)
def get_total(self):
return sum(NumList.get_list(self))
To refresh our memory, the Customer01
subclass expands on the superclass NumList
by adding an additional method - get_total()
.
Getting into the Guts¶
Before going into the guts of Customer01
let's create an object of Customer01
class, apply the methods.
cust02 = Customer01()
print('Initial list of values of cust02:', cust02.get_list())
cust02.add_value(2)
cust02.add_value(20)
cust02.add_value(44)
cust02.add_value(12)
print('Updated list after adding values to it:', cust02.get_list())
val = cust02.remove_value()
print('Updated list after removing value %s is: ' % (val), cust02.get_list())
print('Sum of all the elements of the current list is:', cust02.get_total())
Initial list of values of cust02: [] Updated list after adding values to it: [2, 20, 44, 12] Updated list after removing value 12 is: [2, 20, 44] Sum of all the elements of the current list is: 66
Bare minimum requirements of defining a subclass:
Superclass as a Parameter: While creating a subclass, provide superclass, the class on which the current class will build upon, name as a parameter. In our example, we have created
Customer01
as a subclass of the superclassNumList
.Initializing the Superclass: Python requires to explicitly invoke the superclass constructor (
__init__()
). Invoking the superclass constructor makes sure that the properties of the superclass are properly available to the subclass.🛑 Take a pause and check what happens if you don't invoke the superclass's constructor i.e. try replacing
NumList.__init__(self)
withpass
.
With this initial understanding of class and subclass now we will turn our focus for a bit onto understanding the concept of Inheritance. Don't worry if you are still a bit unclear about the subclass details, we will come back to the subclass again to discuss it further.
Inheritance¶
Inheritance in Real Life¶
Inheritance allows us to build on top of the existing general class(es) and expand the use of these classes by customizing the new classes as we see fit. This concept can be explained using a real-life example.
Superclass¶
In the above example, Vehicles is the most general category or class. We can define a Vehicles as something that are moved by some means and used to transport men or materials. This broad definition lets Vehicles become the superclass that basically encompasses all the vehicle types that we'll define later.
Subclasses¶
In the second layer, we specify vehicles based on the types of surface that they drive on. Also note, how this layer could look entirely different based on the basis on which we want to specify. For example, we could specify the vehicles in two types based on their purpose of use - personal, and commercial.
These Land, Water, Air, and Space vehicles are each a subclass of the superclass - Vehicles.
All these subclasses contain the properties of their superclass - all of these are vehicles that are moved by some means and used to transport men or materials. But in addition to this, they all contain specific properties based on the surface they operate on:
- Land Vehicles have wheels,
- Water Vehicles have propellers and so on.
More Subclasses¶
In the third tier of subclasses, we take Water Vehicles as an example and further specify based on at which level the water vehicles operate. We call them - Surface or Underwater vehicles.
These classes inherit properties from both the superclasses - Vehicles, and Water Vehicles. Meaning, both the surface and underwater vehicles:
- Are moved by some means and used for transportation - properties inherited from Vehicles superclass
- They have some kind of propeller - inherited from Water Vehicle superclass
In addition to these, they have their own specific properties,
- Surface Vehicles have the floating capability
- Under Water Vehicles have the submergible capability
By now hopefully, one key property of inheritance is probably clear that,
The further down we go the more specific the classes become.
🛑 Take the fourth tier from the Vehicles class example and think which properties make them more enhanced and specific for their purpose.
Inheritance in Our Example¶
New Requirement¶
Now let's get back to our coding examples to understand the inheritance concepts by applying them.
To set up a context for that, let's assume that our customer 01 has come back with a new requirement; a bit complicated one compared to the last one. He would like to be able to do two things:
- See each item of the list as a fraction of the sum total of all the items or values.
- Get the largest value along with its corresponding fraction value.
Looking into the requirement we can see that the functionalities are building on top of the functionalities already provided by the previously created classes of NumList
and Customer01
:
NumList
will be the base to create and update a list of numbers.Customer01
will be needed to get the total of all the values.
Considering the flowing relationship among these properties of the classes, we can put these classes into a nice hierarchy: NumList
--> Customer01
--> CustReq01
(New Class yet to be created).
Not that we couldn't bypass Customer01
class but since we already created it let's utilize that class and build on top of it. Also, think if this was an actual commercial application where customer01
is a class defined with hundreds of lines of codes. You wouldn't want to recreate the wheels!
New Subclass: CustReq01
¶
class CustReq01(Customer01):
def __init__(self):
super().__init__()
self.__frac_dict = {}
self.max_item = {}
# A function to calculate fraction of each value to the sum total and populate __frac_dict
def frac_list(self):
for n in self.get_list():
self.__frac_dict.update({n : n/self.get_total()})
return self.__frac_dict
# A function to check the largest value from the list and print as a dictionary item
def frac_max(self):
temp_dict = self.frac_list()
max_val_key = max(temp_dict, key = temp_dict.get)
max_val = max(temp_dict.values())
self.max_item.update({max_val_key : max_val})
return print('Larget value and it\'s fraction to total: ', self.max_item)
x = CustReq01()
x.add_value(2)
x.add_value(20)
print(x.get_list())
x.get_total()
print(x.frac_list())
x.frac_max()
# Getting the largetst value using instance variable
print('Largest item and it\'s fraction to total (from variable): ',x.max_item)
[2, 20] {2: 0.09090909090909091, 20: 0.9090909090909091} Larget value and it's fraction to total: {20: 0.9090909090909091} Largest item and it's fraction to total (from variable): {20: 0.9090909090909091}
In the new subclass CustReq01
, we have added two methods to fulfill the two requirements. But to make these two methods useful we have also added two variables - __frac_dict
and max_item
. Also, we have instantiated the superclass differently than what we have seen in our first subclass - Customer01
. Let's go over these newly introduced features and functionalities now.
Initializing Superclass¶
Two ways of initializing a superclass: explicitly using superclass name, or using
super()
method.
- Using the actual superclass name as: <
SuperClassName
><.
>__init__(self)
. We initializedNumList
this way in our first subclass. - Using a more general format as: <
super()
><.
><__init__()
>. Doing this helps us avoid re-typing the superclass in the class other than as a parameter.
⚠️ Note that super()
method creates a context in which you shouldn't need to put the self
argument. This method invokes the superclass and makes all its properties available to the subclass.
Private vs Public Variables¶
In CustReq01
we have two instance variables: __frac_dict
- an empty dictionary to store future values of fractions, and max_item
- another empty dictionary to store the largest value and its fraction value.
As we have mentioned in the last article, adding two underscores in front of a variable or method makes it private, meaning that the method or variable can't be accessed directly from an object. In the case of CustReq01
, __frac_dict
is created as a private variable. The same principle works for methods also. One can create a private method by adding two underscores as the prefix.
Private variables/methods are typically created in cases where these properties are used by some internal methods inside the class. Thus, leaving those properties as private provides some kind of shield against unintended complications.
Check out this answer from Stackexchange on the benefits of keeping properties private.
Practice¶
Try to think about these two questions:
🛑 Try running both x.max_item
and x.__frac_dict
. If you have run all the codes along with this article till now, can you guess what you would see in the results?
🛑 Also, what would happen if you tried to get the largest value by using x.max_item
before running x.frac_max()
? Can you guess why did you get what you got?
Using Methods Defined Earlier Inside Later Methods¶
Check how the method frac_max()
utilizes method frac_list()
that's defined earlier inside the CustReq01
. It's the very same rule that we follow for using a variable: <self
><.
><method name
>.
Note how we didn't have to mention the self
parameter while using frac_list()
method.
What's Next¶
In this post, we have tried to understand,
- The concept of inheritance
- Understood the syntaxes used to create a subclass
- Apply the concepts that we learned by creating a new subclass
- And finally, we talked a little about instance variables
In the next couple of posts, we will dig a bit deeper into the concepts variables and methods. We will see the different types of variables and methods and how their uses differ from one another.