Backdrop¶
This is the second installment of an ongoing blog post series on Object Oriented Programming (OOP) in Python.
In my last post, I alluded to what is OOP and why you may want to learn OOP even if you may not see the apparent use of it. In case you haven't read it and are curious, take a look at it: Object Oriented Programming in Python - What and Why?.
In this post, we will start going into the guts of some of the OOP concepts and try to deeper our understanding of them.
Building Blocks of a Class¶
To maintain the continuity also to have a concrete example, I will start with the Class that I created in the last article, named NumList
. We will dissect NumList
to see the elements of it. Then we will come back again to a more general discussion on elements of a Class.
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
To start off, let's notice these following things:
- Defining a class starts with the keyword:
class
then followed by a name given to the class and ends with a colon(:)
similar to a function. - A class is usually equipped with these three components:
- A Name: In our case
NumList
, that's used to identify the class. - Properties: A set of features about the class. In
NumList
,__list
is a property. A class may also have no properties. - Methods: The abilities of the class to perform certain tasks. In
NumList
,add_value()
,remove_value
, andget_list()
are the methods. Similar to properties, a class may contain no methods either.
- A Name: In our case
A simple Python class is equipped with three things: Name, Property, and Method
Getting into the Guts of a Class¶
Python Constructor¶
Now let's talk about the first block of code inside NumList
where we defined __init__()
. We will discuss what that method is, explain the notations and keywords used.
__init__()
: This is a special method that's called a constructor.- A constructor automatically runs every time an object is created from the class.
- It loads all the necessary elements of a class necessary to make it usable.
- In the case of
NumList
, the empty list__list
is initiated by the constructor as soon as an object ofNumList
is created. - A constructor is only used within class.
- A constructor must have at least one parameter. In
NumList
,__init__()
has one parameter -self
.
self
: This parameter is used as a self-reference.- Using this parameter makes the variables that belong to the class available.
- It can be called any other names but calling it
self
is a mere convension.
- Dotted Notation: We created the empty list as
self.__list
.- This convention is used to access or create properties of an object:
<ClassName><dot(.)><property name>
. - In
NumList
we by usingself.__list = []
we created a property called__list
that belongs to theNumList
, indicatedself
, and initiated as an empty list.
- This convention is used to access or create properties of an object:
__
: Starting a component name with two underscores makes the component private.- Meaning, the component can only be accessed within the class. It's an implementation of encapsulation.
- Trying to access a private component will result in an error.
- Try:
list01 = NumList()
thenlen(list01.__list)
. It will result in aAttributeError
.
Methods¶
add_value()
and remove_value()
¶
The next three code blocks implement three methods or functions.
The first two methods are basically a recreation of the two functions we created to demonstrate procedural programming in a class context and thus has the following variations in keywords and notations:
- Using
self
as a Parameter: For any method,self
is an obligatory parameter and as discussed earlier,
self
makes all the properties and methods from the class, in our caseNumList
, available to the method.
Parameter in Methods: The methods can have multiple parameters as needed as a regular Python function outside of class context. For example,
add_value()
method has parameter namedval
in addition to the mandatory parameter ofself
.Dotted Notation: According to the convention discussed earlier,
self
refers to the class itself.- So,
self.__list
access to the empty list. Then to use a method we use dotted notation again. - To construct method:
add_value()
, we useappend()
method fromList
class. Since our__list
is a list itself it inherits theappend()
method. So to use this method we use:self.__list.append()
.
- So,
get_list()
¶
Since we initiated the __list
as a hidden component inside NumList
, directly accessing the list is not possible. So we need a method or function to access to it.
get_list()
gives us access to the hidden parameter__list
.
Going Beyond a Single Class¶
Subclass¶
Now let's assume that after delivering our product, NumList
, to our customer we have got a new customer from referral. This customer wants the same list, and capabilities but also needs:
- An ability to get the sum of the numbers in the list
So, how would we do that?
One obvious way is to copy the codes of NumList
and add another method to it, let's call it get_total()
. But what if we keep getting new customers or orders with different additional feature requests? Very soon this process of copying and modifying is not going to be an efficient solution.
OOP has a solution to address this problem in an efficient manner: creating subclasses. Creating subclasses has two distinct advantages:
- Reuse of Codes: A subclass inherits all the methods from the superclass and hence we don't have to recreate the methods.
- Customization: A subclass can contain new methods and properties. That makes it easy to customize a solution while keeping the basics from the superclass unchanged.
Example¶
Now let's create a subclass named NumListExt01
to extend functionality of NumList
class.
class NumListExt01(NumList):
def __init__(self):
NumList.__init__(self)
def get_total(self):
return sum(NumList.get_list(self))
cust02 = NumListExt01()
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
Notice how NumList
was utilized by the subclass and customized without disturbing the superclass:
♻️ Subclass
NumListExt01
inherited the methods (add_value()
,remove_value()
,get_list()
) from super classNumList
.➕ Subclass
NumListExt01
has an additional method,get_total()
, which exists only in this class and thus only available to our new customer.
What's Next?¶
In this post we have seen
- How we can construct a Python class
- How we can extend an existing class by adding subclass.
In the next post, we will discuss more about subclass, get into the guts of subclass, and discuss about inheritance in Python with examples.