Object Oriented Programming In Python

Object-Oriented Programming is a fundamental programming paradigm based on the concepts of objects and classes that can perform certain operations through their methods. We'll get into more details about OOP in this notebook.

Note: For this notebook, one should have some understanding of objects and classes.

Python Classes

A class is more or less a blueprint of an object. A class defines all the attributes and methods a certain object can attain. For example, a class for a student could contain attributes such as name, date of birth, phone number etc. and also contain methods which could perform any desired operations on these attributes.

An important building block of the class is the class' constructor (i.e the __init__ method). This method is the default method called upon instantiation (creating an instance/object of that class) and is usually where we'll define our attributes..

Let us understand all the above through an example.

In [ ]:
class Student():
  def __init__(self, name):
    self.name = name

Instantiation of a new Python object ...

In [ ]:
x = Student("Mark")
print(x)
# Printing the type of the object to show which class it belongs to
print(type(x))
# Retrieving the attribute "name" from the object
print(x.name)
<__main__.Student object at 0x7f43f0d5a510>
<class '__main__.Student'>
Mark

Python Objects

An Object (Instance) is an instantiation of a class. As explained earlier, classes are blueprints that we can use to create objects/instances. The example shown above of the student class explains how to create a class and instantiate an object. Let us add more attributes to that class and test it's functionality.

In [ ]:
import datetime
class Student():
  def __init__(self, name, dob, number):
    self.name = name
    self.birth_date = dob
    self.phone_number = number
In [ ]:
x = Student("Mark", "07/25/94", "123456789")
print(x)
print(type(x))
print("Student Name: ", x.name)
print("Student Birth Date: ", x.birth_date)
print("Student Phone Number: ", x.phone_number)
<__main__.Student object at 0x7f43f0c61f50>
<class '__main__.Student'>
Student Name:  Mark
Student Birth Date:  07/25/94
Student Phone Number:  123456789

Python Methods

Methods are special types of functions. There is a difference between functions and methods and that is due to the fact that methods are only specific to the classes they belong to whereas functions have more of a global scope. Let us put all this into action and create a method in our student class to calculate the student's age from his/her date of birth.

In [ ]:
class Student():
  def __init__(self, name, dob, number):
    self.name = name
    self.birth_date = dob
    self.phone_number = number

  def age_calculator(self):
    current_date = datetime.datetime.now().date()
    student_birthdate = datetime.datetime.strptime(self.birth_date, "%m/%d/%y").date()

    delta = current_date - student_birthdate
    age = int(delta.days/365)
    return age

I have used Python datetime package in above code. To learn more about Python datetime checkout here.

In [ ]:
x = Student("Mark", "07/25/94", "123456789")
print(x)
print(type(x))
print("Student Name: ", x.name)
print("Student Birth Date: ", x.birth_date)
print("Student Phone Number: ", x.phone_number)
print("Student Age: ", x.age_calculator())
<__main__.Student object at 0x7f43f0bc3210>
<class '__main__.Student'>
Student Name:  Mark
Student Birth Date:  07/25/94
Student Phone Number:  123456789
Student Age:  27

Voila! Now you have an idea about the building blocks of OOP in Python. Now let us discuss the Object Oriented Programming advantages.

Inheritance In Python

One of the main reasons developers tend to use OOP paradigm is inheritance. To understand inheritance, Let us build up on our previous student example and extend it for university students. Now there can be different type of students in a university. For example:

  • Undergraduate Students
  • Postgraduate Students
  • Exchange Students (Undergraduate and Postgraduate)

For above cases, we can write a class for each type, however, this will make our code very messy and unreadable. Instead, the inheritance feature that OOP provides will help us create abstract parent class from which children classes could inherit the common features. So what are the common attributes among all the students type defined above?

  • name
  • date of birth
  • phone number
  • address

Now, Let us write a basic student "parent" class that will include the above basic attributes and then later we will write the child classes for every student type.

In [ ]:
class Student():
  def __init__(self, name, dob, number, address):
    self.name = name
    self.birth_date = dob
    self.number = number
    self.address = address

Now that we have created the parent abstract class, let us see how children subclasses can inherit this information and also can have their own unique attributes.

In [ ]:
class Undergraduate(Student):

  def __init__(self, name, dob, number, address, sat_score):
    Student.__init__(self, name, dob, number, address)
    self.sat_score = sat_score


class Postgraduate(Student):
  def __init__(self, name, dob, number, address, bachelors_gpa):
    Student.__init__(self, name, dob, number, address)
    self.bachelors_gpa = bachelors_gpa
In [ ]:
x = Undergraduate("Mark", "07/21/94", "123456789", "12 Hollywood St.", "1450")
y = Postgraduate("Sam", "04/15/89", "987654321", "75 Hollywood St.", "3.50")

print(type(x))
print("Student Name: ", x.name)
print("Student Birth Date: ", x.birth_date)
print("Student Phone Number: ", x.number)
print("Student's Address: ", x.address)
print("Student's SAT Score: ", x.sat_score)

print('-----------------------------------')
print(type(y))
print("Student Name: ", y.name)
print("Student Birth Date: ", y.birth_date)
print("Student Phone Number: ", y.number)
print("Student's Address: ", y.address)
print("Student's Bachelor's GPA: ", y.bachelors_gpa)
<class '__main__.Undergraduate'>
Student Name:  Mark
Student Birth Date:  07/21/94
Student Phone Number:  123456789
Student's Address:  12 Hollywood St.
Student's SAT Score:  1450
-----------------------------------
<class '__main__.Postgraduate'>
Student Name:  Sam
Student Birth Date:  04/15/89
Student Phone Number:  987654321
Student's Address:  75 Hollywood St.
Student's Bachelor's GPA:  3.50

Without inheritance, your code would have looked something like this ...

class Undergraduate():
  def __init__(self, name, dob, number, address, sat_score):
    self.name = name
    self.birth_date = dob
    self.number = number
    self.address = address
    self.sat_score = sat_score


class Postgraduate():
  def __init__(self, name, dob, number, address, bachelors_gpa):
    self.name = name
    self.birth_date = dob
    self.number = number
    self.address = address
    self.bachelors_gpa = bachelors_gpa

Now imagine what would have happened if you had 10 or more of the these above classes and not just two.

Polymorphism in Python

Polymorphism is the ability to use a common interface for multiple data types or forms. In examples below, we will observe how the same method can have different forms or outputs depending on their class. Let us take the classes from our previous example and add method "highest_degree" that can take different forms.

In [ ]:
class Student():
  def __init__(self, name, dob, number, address):
    self.name = name
    self.birth_date = dob
    self.number = number
    self.address = address
  
  def highest_degree(self):
    print("Abstract Base Method")

class Undergraduate(Student):

  def __init__(self, name, dob, number, address, sat_score):
    Student.__init__(self, name, dob, number, address)
    self.sat_score = sat_score
  
  def highest_degree(self):
    return "Highest Degree is High Level Education."


class Postgraduate(Student):
  def __init__(self, name, dob, number, address, bachelors_gpa):
    Student.__init__(self, name, dob, number, address)
    self.bachelors_gpa = bachelors_gpa
  def highest_degree(self):
    return "Highest Degree is a bachelor's degree."
In [ ]:
x = Undergraduate("Mark", "07/21/94", "123456789", "12 Hollywood St.", "1450")
y = Postgraduate("Sam", "04/15/89", "987654321", "75 Hollywood St.", "3.50")

print(x.highest_degree())
print(y.highest_degree())
Highest Degree is High Level Education.
Highest Degree is a bachelor's degree.

As you can see, even though we used the same method call, the output was different for each and every class due to the fact that we have overridden the method called highest_degree and made it custom to each and every class to demonstrate the concept that is polymorphism.

Enacpsulation in Python

We can also restrict access to methods and variables in OOP. This will prevent the data from being modified directly..

One side note here, In python, we denote the private attributes using the uderscore _.

In [1]:
class Car():
  def __init__(self, price):
    self.__price = price
  
  def set_price(self, price):
    self.__price = price

  def selling_price(self):
    return self.__price
In [2]:
x = Car(1500)

print("Initial price: ", x.selling_price())
Initial price:  1500

Let us try to change the price by accessing the private attribute and see if it works.

In [ ]:
x.__price = 2000
print("Price change attempt: ", x.selling_price())
Initial price:  1500
Price change attempt:  1500

As we can see, it did'nt work. Now let us try changing the price using the setter function set_price that we created for this purpose.

In [ ]:
x.set_price(2000)
print("New price: ", x.selling_price())
New price:  2000

Note that you can also make an attribute private and not create a setter method for it that would make the attribute "price" unchangeable.

Ok, So far so good. We are now approaching the end of this introductory tutorial on Object oriented Programming. Let us end this tutorial by summarizing the Uses and advantages of OOP.

Uses of OOP

The uses of OOP are across all sorts of applications. Some of those are:

  • Modelling databases.
  • Creating Machine Learning, Neural Network models.
  • Creating libraries and frameworks to use and/or publish.
  • Modularizing applications for ease of organization.

Advantages of OOP

  • Makes the program easy to understand as well as efficient.
  • Code can easily be reused.
  • Data is safe and secure with data abstraction and encapsultion.
  • Polymorphism allows us to use the same interface for more than one funcitonality/output.