Educative OOP in Python
Educative OOP in Python
• Procedural Programming
• Enter: Object-Oriented Programming
• Anatomy of Objects and Classes
• User-De ned Data Types
Procedural Programming #
If you’re here, you’re probably familiar with the basics of programming
already and have used methods in your programs at some point.
Programming isn’t much use if you can’t model real-world scenarios using
code, right? This is where Object-Oriented Programming comes into play.
The basic idea of OOP is to divide a sophisticated program into a bunch
of objects talking to each other.
A
bicycle
object
A
person
object
A
bulb
object
Many other objects serve application logic and have no direct, real-world
parallels. They manage things like authentication, templating, request
handling, or any of the other myriad features needed for a practical
application.
Think about the real-world objects around you. What are the characteristics of
these objects? Take the example of a light bulb. It has a state, i.e., either it is on
or off. It also has a behavior, i.e., when you turn it on it lights up, and when
turned off, it stops spreading light. To conclude this, one can say:
Objects are a collection of data and their behaviors.
Interesting! Isn’t it? But the question is “where do these objects come from?”
The below illustration shows what a LightBulb class should look like:
class Bulb
onOff State of Bulb
turnOn( )
Behavior of Bulb
turnOff( )
1 of 3
class Bulb
onOff stores true now
bulb is lit
2 of 3
class Bulb
onOff stores false now
turnOn( )
turnOff( ) method called
bulb is off
3 of 3
From the above illustration, you can see that the state of the object is generally
modeled using variables in a class, and the behavior is modeled using
methods.
There can be many different objects of the same class. Each can be in an
independent state, but they all share the same behavior and characteristics.
onOff
)
n( e
r nO fals
tu or
turnOf true
f( )
Bulb boolean
User-defined Primitive
data type data type
This lesson is about the most popular Object-Oriented Programming languages and how convenient Python is.
• Languages Round-Up
• Comparing Code in Multiple Languages
Languages Round-Up #
Nowadays, there are a lot of object-oriented programming languages being
used around the globe and Python is one of them.
Each language has its own pros and cons. A language is chosen depending on
the nature of the task to be performed. Below, we can find some of the most
popular programming languages with their core strengths and common
applications:
#
Python: Data science, machine learning, and artificial intelligence.
Java: Mostly used for enterprise-level and Android software
development.
JavaScript: Rapid and productive web development.
Ruby: Web application development.
C++: System software development.
C#: Game development, web forms, and web applications development.
print(add(2, 4))
As you can see in the codes above, for Python we simply wrote the add
function without making any class or importing any libraries, and called it in
our main function just like in a procedural language.
However, when you look at the code for C++, Java, and C#, you’ll see that all
three are a bit more complex and extensive. If you tried removing some
keywords like Class , static , and int in these codes, it would generate an
error and the codes won’t compile. So, if you intend to write even a simple
add or sum function in C++, C#, or Java, you’ll always face a base level of
complexity.
Having said this, there are some people who are skeptical about the use of
object-oriented language. They believe it makes the overall program size more
complex. As we’ve shown in our example above, even writing a simple sum
function can become a complicated task. They also argue that the details of
object-oriented programming, its syntax, and peculiarities, are difficult for the
programmer to learn, resulting in a steep learning curve.
This lesson introduces you to the concept of objects and classes and the bene ts of using them.
• A Brief Encounter
• Objects and Classes
• Properties
• Methods
• Bene ts of Objects and Classes
A Brief Encounter #
We can see objects everywhere in our surroundings. These objects have
certain properties that define them. There are certain behaviors that these
objects perform on their own, and there are actions that can be performed on
them as well.
Let’s take the example of a company employee. An employee has the following
properties or attributes:
ID
Salary
Department
In a company, each worker has a different name, salary, and department but
the type of each worker is employee. So, there is a generic blueprint for each
worker working in the company, but each one of them has different attributes
associated with them.
Having the same blueprint but different properties are the basis for classes
and objects.
ID: 72655
Salary: 3000
Department: Software
Mark
ID: 72644
Salary: 4000
Department: Marketing
Chris
Properties
ID
salary
department
Methods
tax()
salaryPerDay()
employee class
Properties #
Properties are variables that contain information regarding the object of a
class. An employee object will have an ID , a salary , and the department as its
properties. New properties could be added to be a part of an object of the
employee class.
Methods #
Methods are like functions that have access to properties (and other methods)
of a class. Methods can accept parameters and return values. They are used to
perform an action on an object of a class. In the example above, we have
tax() and salaryPerDay() as class methods.
• Declaration
• Naming Rules
• Creating a Class Object
Declaration #
In Python, classes are defined as follows:
class ClassName:
pass
The class keyword tells the compiler that we are creating a custom class, and
this is followed by the class name and : sign.
All the properties and methods of the class will be defined within the class
scope.
Naming Rules #
The following rules must be adhered to when naming classes:
class MyClass:
pass
This is just a basic implementation of a Python class and doesn’t serve any
particular purpose as it does not contain any properties or methods.
In the next few lessons, we’ll learn the implementation of Python classes in
detail.
Implementing Properties in a Class
In this lesson, you will learn how to create properties, both in classes and outside of classes, and how to access
them.
Properties
ID
salary
department
Methods
Employee class
With_None Without_None
salary = None
department = None
Note that if you do not initialize the values of properties, the Python code
will not compile. Initializing the values of properties inside the class is
necessary.
The code in the second tab will not compile since the properties in the class
have not been initialized.
object.property
initializing assigning
class Employee:
# defining the properties and assigning values to them
ID = 3789
salary = 2500
department = "Human Resources"
ID: Steve
salary: 2500
department: Human
Resources
Steve
1 of 3
ID: Steve
salary: 2500
department: Human
Resources
+
title: Manager
Steve
adding extra property in Steve
2 of 3
ID: Steve
salary: 2500
department: Human
Resources
title: Manager
Steve
extra property added to Steve
3 of 3
Note: The property, title , will only be added to Steve and all other
future objects will only have the properties which are declared in the
class.
class Employee:
# defining the properties and assigning them None
ID = None
salary = None
department = None
In this lesson, the world of initializers is explored. You'll learn why they are necessary for a class.
• What is an Initializer?
• De ning Initializers
• Initializer with Optional Parameters
What is an Initializer? #
As the name suggests, the initializer is used to initialize an object of a class.
It’s a special method that outlines the steps that are performed when an object
of a class is created in the program. It’s used to define and assign values to
instance variables. We will discuss the concept of instance variables in the
next lesson. For now, just focus on the syntax.
The double underscores mean this is a special method that the Python
interpreter will treat as a special case.
The initializer is a special method because it does not have a return type.
The first parameter of __init__ is self , which is a way to refer to the object
being initialized. We will discuss this later.
It is always a good practice to define it as the first member method in the class
definition.
De ning Initializers #
Initializers are called when an object of a class is created. See the example
below:
class Employee:
# defining the properties and assigning them None
def __init__(self, ID, salary, department):
self.ID = ID
self.salary = salary
self.department = department
You can also have a default initializer that has all properties as optional. In
this case, all the new objects will be created using the properties initialized in
the initializer definition.
Below is an example where one Employee class object is created without the
initializer parameters and one is created with the initializer parameters.
class Employee:
# defining the properties and assigning None to them
def __init__(self, ID=None, salary=0, department=None):
self.ID = ID
self.salary = salary
self.department = department
In the next lesson, we will learn about the differences between class and
instance variables.
Class and Instance Variables
In this lesson, we'll learn about the concepts of instance variables and class variables, and how to use class
variables correctly.
• De nitions
• Class Variables
• Instance Variables
• De ning Class Variables and Instance Variables
• Wrong Use of Class Variables
• Using Class Variables Smartly
• Explanation
class variables
instance variables
Properties
class instance
variables variables
De nitions #
Class Variables #
The class variables are shared by all instances or objects of the classes. A
change in the class variable will change the value of that property in all the
objects of the class.
Instance Variables #
The instance variables are unique to each instance or object of the class. A
change in the instance variable will change the value of the property in that
specific object only.
class Player:
teamName = 'Liverpool' # class variables
p1 = Player('Mark')
p2 = Player('Steve')
print("Name:", p1.name)
print("Team Name:", p1.teamName)
print("Name:", p2.name)
print("Team Name:", p2.teamName)
class Player:
formerTeams = [] # class variables
teamName = 'Liverpool'
def __init__(self, name):
p1 = Player('Mark')
p2 = Player('Steve')
p1 = Player('Mark')
p1.formerTeams.append('Barcelona')
p2 = Player('Steve')
p2.formerTeams.append('Chelsea')
print("Name:", p1.name)
print("Team Name:", p1.teamName)
print(p1.formerTeams)
print("Name:", p2.name)
print("Team Name:", p2.teamName)
print(p2.formerTeams)
In the example above, while the instance variable name is unique for each and
every object of the Player class, the class variable, formerTeams , can be
accessed by any object of the class and is updated throughout. We are storing
all players currently playing for the same team, but each player in the team
may have played for different former teams. To avoid this issue, the correct
implementation of the example above will be the following:
class Player:
teamName = 'Liverpool' # class variables
p1 = Player('Mark')
p1.formerTeams.append('Barcelona')
p2 = Player('Steve')
p2.formerTeams.append('Chelsea')
print("Name:", p1.name)
print("Team Name:", p1.teamName)
print(p1.formerTeams)
print("Name:", p2.name)
print("Team Name:", p2.teamName)
print(p2.formerTeams)
Now the property formerTeams is unique for each Player class object and can
only be accessed by that unique object.
class Player:
teamName = 'Liverpool' # class variables
teamMembers = []
p1 = Player('Mark')
p2 = Player('Steve')
print("Name:", p1.name)
print("Team Members:")
print(p1.teamMembers)
print("")
print("Name:", p2.name)
print("Team Members:")
print(p2.teamMembers)
Explanation #
In the example above, we’ve defined a class variable teamMembers , which
is a list that will be shared by all the objects of the class Player .
This list, teamMembers , will contain names of all the instances created of
the Player class.
As you can see in line 8, whenever a new object is created, its name is
appended in teamMembers .
In line 16 and line 20, we can see that teamMembers is accessed by p1 and
p2 respectively and both produce the same output.
In the next lesson, we’ll learn about implementing methods in a class.
Implementing Methods in a Class
In this lesson, you will get to know about the role of methods in classes and what method overloading is.
Now that we have learned about adding properties to a class, we will move
towards the interaction between these properties and other objects. This is
where methods come into play. There are three types of methods in Python:
1. instance methods
2. class methods
3. static methods
We will be discussing the instance methods in this lesson since they are used
the most in Python OOP.
Note: We will be using the term methods for instance methods in our
course since they are most commonly used. Class methods and static
methods will be named explicitly as they are.
These methods can either alter the content of the properties or use their
values to perform a particular computation.
We will extend the Employee class example that we studied in the previous
lesson by adding methods to it.
Properties
ID
salary
department
Methods
tax()
salaryPerDay()
Employee class
Method Parameters #
Method parameters make it possible to pass values to the method. In Python,
the first parameter of the method should ALWAYS be self (will be discussed
below) and this is followed by the remaining parameters.
Return Statement #
The return statement makes it possible to get the value from the method.
There is no need to specify the return type since data types are not
specified in Python.
class Employee:
# defining the initializer
def __init__(self, ID=None, salary=None, department=None):
self.ID = ID
self.salary = salary
self.department = department
def tax(self):
return (self.salary * 0.2)
def salaryPerDay(self):
return (self.salary / 30)
class Employee:
# defining the properties and assigning them None to the
def __init__(self, ID=None, salary=None, department=None):
self.ID = ID
self.salary = salary
self.department = department
# method overloading
def demo(self, a, b, c, d=5, e=None):
print("a =", a)
print("b =", b)
print("c =", c)
print("d =", d)
print("e =", e)
def salaryPerDay(self):
return (self.salary / 30)
print("Demo 2")
Steve.demo(1, 2, 3, 4)
print("\n")
print("Demo 3")
Steve.demo(1, 2, 3, 4, 5)
In the code above, we see the same method behaving differently when
encountering different types of inputs.
An obvious benefit is that the code becomes simple and clean. We don’t have
to keep track of different methods.
Makes
Allows the
code
implementation
cleaner Overloading
of
and
polymorphism
readable
Same
method
name
saves
memory
In the next lesson, we will discuss the utility of class methods and static
methods.
Class Methods and Static Methods
Method
instance class
static
methods methods
methods
Class Methods #
Class methods work with class variables and are accessible using the class
name rather than its object. Since all the class objects share the class
variables, the class methods are used to access and modify class variables.
Class methods are accessed using the class name and can be accessed
without creating a class object.
Syntax #
To declare a method as a class method, we use the decorator @classmethod
and cls is used to refer to the class just like self is used to refer to the object
of the class. You can use any other name instead of cls , but cls is used as per
convention, and we will continue to use this convention in our course as well.
Note: Just like instance methods, all class methods have at least one
argument, cls .
class MyClass:
classVariable = 'educative'
@classmethod
def demo(cls)
return cls.classVariable
class Player:
teamName = 'Liverpool' # class variables
@classmethod
def getTeamName(cls):
return cls.teamName
print(Player.getTeamName())
In line 7, we have used the @classmethod decorator to define getTeamName as a
class method and in line 12, we are calling that method using the class name.
Static Methods #
Static methods are methods that are usually limited to class only and not
their objects. They have no direct relation to the class variables or instance
variables. They are used as utility functions inside the class or when we do
not want the inherited classes, which we will study later, to modify a method
definition.
Static methods can be accessed using the class name or the object name.
Syntax #
To declare a method as a static method, we use the decorator @staticmethod . It
does not use a reference to the object or class, so we do not have to use self
or cls . We can pass as many arguments as we want and use this method to
perform any function without interfering with the instance or class variables.
class MyClass:
@staticmethod
def demo()
print("I am a static method")
class Player:
teamName = 'Liverpool' # class variables
@staticmethod
def demo():
print("I am a static method.")
p1 = Player( lol )
p1.demo()
Player.demo()
Static methods do not know anything about the state of the class, i.e., they
cannot modify class attributes. The purpose of a static method is to use its
parameters and produce a useful result.
Let’s suppose that there is a class, BodyInfo , containing information about the
physical attributes of a person. We can make a static method for calculating
the BMI of any given weight and height:
class BodyInfo:
@staticmethod
def bmi(weight, height):
return weight / (height**2)
weight = 75
height = 1.8
print(BodyInfo.bmi(weight, height))
This was all about class and static methods. In the next lesson, we will learn
about access modifiers.
Access Modifiers
In this lesson, you will learn about the private, public, and protected data members in Python.
• Public Attributes
• Private Attributes
• Private Properties
• Code Explanation
• Private Methods
• Code Explanation
• Accessing Private Attributes in the Main Code
• Not So Protected
There are two types of access modifiers in Python. Let’s take a look at them
one by one.
Public Attributes #
Public attributes are those that be can be accessed inside the class and
outside the class.
class Employee:
def __init__(self, ID, salary):
# all properties are public
self.ID = ID
self.salary = salary
def displayID(self):
print("ID:", self.ID)
In the code above, the properties ID and salary , and the method displayID()
are public as they can be accessed in the class as well as the outside the class.
ID
salary
Main
class
Private Attributes #
Private attributes cannot be accessed directly from outside the class but
can be accessed from inside the class.
The aim is to keep it hidden from the users and other classes. Unlike in many
different languages, it is not a widespread practice in Python to keep the data
members private since we do not want to create hindrances for the users. We
can make members private using the double underscore __ prefix
Trying to access private attributes in the main code will generate an error. An
example of this is shown below:
Private Properties #
Let’s see a code example for implementing private properties:
private_properties
class Employee:
def __init__(self, ID, salary):
self.ID = ID
self.__salary = salary # salary is a private property
Code Explanation #
In the code above, ID is a public property but __salary is a private
property, so it cannot be accessed outside the class.
To ensure that no one from the outside knows about this private property,
the error does not reveal the identity of it.
'Employee'
object has no Calling
Employee attribute
'__salary'
Employee to
access __salary
ID
__salary
Main
Class
Private Methods #
Let’s see a code example for implementing private methods:
private_methods
class Employee:
def __init__(self, ID, salary):
self.ID = ID
self.__salary = salary # salary is a private property
Code Explanation #
ID is a public property, so it can be accessed from outside and inside the
class.
__salary is a private property, so it cannot be accessed from outside the
class but can be accessed from inside the class.
To ensure that no one from the outside knows about this private property,
the error does not reveal the identity of it.
'Employee'
calling
object has no
Employee to
Employee attribute
'__displayID()'
access
__displayID()
displaySalary()
__displayID()
ID
__salary Main
Class
Note: Methods are usually public since they provide an interface for the
class properties and the main code to interact with each other.
Properties and methods with __ prefix are usually present to make sure that
the user does not carelessly access them. Python allows for a free hand to the
user to avoid any future complications in the code. If the user believes it is
class Employee:
def __init__(self, ID, salary):
self.ID = ID
self.__salary = salary # salary is a private property
Not So Protected #
Protected properties and methods in other languages can be accessed by
classes and their subclasses which will be discussed later in the course. As we
have seen, Python does not have a hard rule for accessing properties and
methods, so it does not have the protected access modifier.
1
The attributes of a class can be divided into the following two parts:
COMPLETED 0%
1 of 5
Challenge 1: Square Numbers and Return Their Sum
In this challenge, you need to implement a method which squares passing variables and returns their sum.
• Problem Statement
• Task 1
• Task 2
• Coding Exercise
Problem Statement #
Implement a class Point that has three properties and a method. All these
attributes (properties and methods) should be public. This problem can be
broken down into two tasks:
Task 1 #
Implement a constructor to initialize the values of three properties: x , y , and
z.
Task 2 #
Implement a method, sqSum() , in the Point class which squares x , y , and z
and returns their sum.
Sample Properties
1, 3, 5
35
sqSum(x, y, z)
2 3 4
1 of 3
sqSum(x,y,z)
2 3 4
4 9 16
2 of 3
sqSum(x,y,z)
2 3 4
4 + 9 + 16
29
3 of 3
Coding Exercise #
First, take a close look and design a step-by-step algorithm like in the slides
above before jumping to its implementation. This problem is intended for
your practice, so initially try to solve it on your own. If you get stuck, you can
always refer to the solution provided in the solution section.
Good luck!
class Point:
def __init__(self):
self.x = x
self.y = y
self.z = z
def sqSum(self):
pass
The solution will be explained in the next lesson.
Solution Review 1: Square Numbers and Return Their
Sum
This lesson provides a solution review for the 'Square Numbers and Return Their Sum' challenge.
• Solution
• Explanation
• Task 1
• Task 2
Solution #
class Point:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def sqSum(self):
a = self.x * self.x
b = self.y * self.y
c = self.z * self.z
return(a + b + c)
obj1 = Point(4, 5, 6)
print(obj1.sqSum())
Explanation #
Let’s look at the solution for each task one by one.
Task 1 #
We passed parameters and assigned them to their corresponding
properties.
Task 2 #
We calculated the squares of the properties by multiplying them with
itself and stored them in a variable.
We then added all the squared numbers.
At last, we returned their sum.
In this exercise, you have to calculate the student's total marks using the concept of Classes.
• Problem Statement
• Task 1
• Task 2
• Task 3
• Coding Exercise
Problem Statement #
Implement a class Student that has three properties and a method. All these
attributes (properties and methods) should be public. This problem can be
broken down into two tasks:
Task 1 #
Implement a constructor to initialize the values of four properties: name , phy ,
chem and, bio .
Task 2 #
Implement a method, totalObtained , in the Student class that calculates total
marks of a student.
Sample Properties
name = Mark
phy = 80
chem = 90
bio = 40
Sample Method Output
obj1.Total()=210
Task 3 #
Using the totalObtained method, implement another method, percentage , in
the Student class that calculates the percentage of students marks. Assume
that the total marks of each subject are 100. So, the combined marks of three
subjects are 300.
M arks Obtained
P ercentage = T otal M arks
× 100
Sample Input
phy = 80
chem = 90
bio = 40
Sample Output
70
Coding Exercise #
First, take a close look and design a step-by-step algorithm before jumping to
the implementation. This problem is designed for your practice, so initially try
to solve it on your own. If you get stuck, you can always refer to the solution
provided in the solution review.
Good Luck!
class Student:
def __init__(self):
pass
def totalObtained(self):
pass
def Percentage(self):
pass
The solution will be explained in the next lesson.
Solution Review 2: Calculate the Student's
Performance
This review provides a detailed analysis to solve the 'Calculate the Student's Total Marks' challenge.
• Solution
• Explanation
Solution #
class Student:
def __init__(self, name, phy, chem, bio):
self.name = name
self.phy = phy
self.chem = chem
self.bio = bio
def totalObtained(self):
return(self.phy + self.chem + self.bio)
def Percentage(self):
return((self.totalObtained() / 300) * 100)
Explanation #
In line 3-6, we have initialized name , phy , chem , and bio as public
properties in the class initializer.
In this exercise, you have to implement a calculator which can perform addition, subtraction, multiplication, and
division.
• Problem Statement
• Task 1
• Initializer
• Properties
• Task 2
• Methods
• Input
• Output
• Sample Input
• Sample Output
• Coding Exercise
Problem Statement #
Write a Python class called Calculator by completing the tasks below:
Task 1 #
Initializer #
Properties #
num1
num2
Task 2 #
Methods #
Input #
Pass numbers (integers or floats) in the initializer.
Output #
addition, subtraction, division, and multiplication
Sample Input #
Sample Output #
104
84
940
9.4
Coding Exercise #
First, take a close look and design a step-by-step algorithm before jumping to
the implementation. This problem is designed for your practice, so initially try
to solve it on your own. If you get stuck, you can always refer to the solution
provided in the solution review.
Good luck!
class Calculator:
def __init__(self):
pass
def add(self):
pass
def subtract(self):
pass
def multiply(self):
pass
def divide(self):
pass
This review provides a detailed analysis to solve the 'Implement a Calculator Class' challenge.
• Solution
• Explanation
Solution #
class Calculator:
def __init__(self, num1, num2):
self.num1 = num1
self.num2 = num2
def add(self):
return (self.num2 + self.num1)
def subtract(self):
return (self.num2 - self.num1)
def multiply(self):
return (self.num2 * self.num1)
def divide(self):
return (self.num2 / self.num1)
Explanation #
We have implemented the Calculator class which has the two properties
num1 and num2.
In the initializer, at line 3-4, we have initialized both properties, num1
and num2 .
In this lesson, you will get familiar with a very important aspect of Object Oriented Programming called data
hiding.
• Introduction
• A Real Life Example
• Components of Data Hiding
Introduction #
In the previous chapter, you got familiar with the concept of objects and
classes.
In OOP, objects and classes are the fundamental entities. Objects are created
using classes. One can observe that classes contain properties and that objects
are created to manipulate and access these properties. To make this object-
oriented system more reliable and error free, it is a good practice to limit
access to the class members at times.
The purpose is to implement classes in such a way that the instances (objects)
of these classes should not be able to cause any unauthorized access or change
in the original contents of a class. One class does not need to know anything
about the underlying algorithms of another class. However, the two can still
communicate.
A Real Life Example #
Let’s apply this to a real-world scenario. Take the doctor-patient model. In the
case of an illness, the patient consults the doctor, after which he or she is
prescribed the appropriate medicine.
The patient only knows the process of going to the doctor. The logic and
reasoning behind the doctor’s prescription of a particular medicine are
unknown to the patient. A patient will not understand the medical details the
doctor uses to reach his/her decision on the treatment.
This is a classic example of the patient class interacting with the doctor class
without knowing the inner workings of the doctor class.
1 of 5
The patient
consults the
doctor.
2 of 5
The doctor
diagnoses the
illness
3 of 5
The doctor
prescribes a
medicine
4 of 5
The medicine
works!
5 of 5
1. Encapsulation
2. Abstraction
We will leave the abstraction part to a later chapter. In this section, we will
cover the encapsulation part in detail.
Information
Hiding
Encapsulation Abstraction
This was pretty much all about information hiding and the related techniques.
In the next lesson, you will learn about Encapsulation.
Encapsulation
In this lesson, you'll get familiar with one of the components of data hiding, encapsulation.
• De nition
• Advantages of Encapsulation
De nition #
Encapsulation is a fundamental programming technique used to achieve data
hiding in OOP.
Depending upon this unit, objects are created. Encapsulation is usually done
to hide the state and representation of an object from outside. A class can be
thought of as a capsule having methods and properties inside it.
Me
tho
ds
Var
iab
les
Class
Well, the answer to this is simple. One has to implement public methods to let
the outside world communicate with this class. These methods are called
getters and setters. We can also implement other custom methods.
Private
Properties
Public methods
which are
accessible from the
outside
Class
Advantages of Encapsulation #
Classes make the code easy to change and maintain.
Properties to be hidden can be specified easily.
We decide which outside classes or functions can access the class
properties.
In the next lesson, we’ll learn about some special methods called getters and
setters.
Getters and Setters
value setProperty(value)
Example #
Let’s write get and set methods for __username in our User class:
class User():
def __init__(self, username=None): # defining initializer
self.__username = username
def getUsername(self):
return (self.__username)
Steve = User('steve1')
print('Before setting:', Steve.getUsername())
Steve.setUsername('steve2')
print('After setting:', Steve.getUsername())
For this property to interact with any external environment, we have to use
the get and set functions. The get function, getUsername() , returns the value of
__username and the setUsername(x) sets the value of __username equal to the
parameter x passed.
In this lesson, you will get a rmer understanding of encapsulation in Python with the help of examples.
• A Bad Example
• A Good Example
• Explanation
The goal is to prevent this bound data from any unwanted access by the code
outside this class. Let’s understand this using an example of a very basic User
class.
Whenever a new user comes, a new object can be created by passing the
userName and password to the constructor of this class.
class User
userName
password
login()
User
A Bad Example #
Now it is time to implement the above discussed User class.
class User
userName
password
login()
class User:
def __init__(self, userName=None, password=None):
self.userName = userName
self.password = password
def login(self, userName, password):
if ((self.userName.lower() == userName.lower())
and (self.password == password)):
print("Access Granted!")
else:
print("Invalid Credentials!")
In the above coding example, we can observe that anyone can access, change,
or print the password and userName fields directly from the main code. This is
dangerous in the case of this User class because there is no encapsulation of
the credentials of a user, which means anyone can access their account by
manipulating the stored data. So, the above code did not follow good coding
practices.
A Good Example #
Let’s move on to a better implementation of the User class!
class User
__userName
__password
login()
In the code below an AttributeError will be thrown because the code outside
the User class tried to access a private property.
class User:
def __init__(self, userName=None, password=None):
self.__userName = userName
self.__password = password
# created a new User object and stored the password and username
Steve = User("Steve", "12345")
Steve.login("steve", "12345") # Grants access because credentials are valid
# does not grant access since the credentails are invalid
Steve.login("steve", "6789")
Steve.__password # compilation error will occur due to this line
Explanation #
In the above example, the fields of __userName and __password are
declared privately using the __ prefix.
We can observe that no one can access, change, or print the __password
and __userName fields directly from the main code. This is a proper
implementation of encapsulation.
Note: For encapsulating a class, all the properties should be private and
any access to the properties should be through methods such as getters
and setters.
This is the concept of encapsulation. All the properties containing data are
private, and the methods provide an interface to access those private
properties.
Now let’s test your understanding of encapsulation with a quick quiz!
Quick Quiz!
1
What are the two components of data hiding?
COMPLETED 0%
1 of 4
In the next lesson, you will have to solve a challenge regarding the
implementation of encapsulation.
Challenge 1: Implement Rectangle Class Using the
Encapsulation
In this challenge, you will implement a 'Rectangle Class Using the Concepts of Encapsulation'.
• Problem Statement
• Task 1
• Task 2
• Task 3
• Coding Exercise
Problem Statement #
You are given a partially completed code of a Rectangle class in the editor.
Implement the class by completing the tasks below.
Task 1 #
Implement a constructor to initialize the values of two private properties:
length and width .
Task 2 #
Implement a method, area() , in the Rectangle class that returns the product
of length and width . See the formula below:
Sample Properties
length = 4
width = 5
Sample Method Output
20
Task 3 #
Implement a method, Perimeter() , in the Rectangle class that returns two
times the sum of length and width . See the formula below:
Sample Properties
length = 4
width = 5
18
Coding Exercise #
First, take a close look and design a step-by-step algorithm before trying the
implementation. This problem is designed for your practice, so initially try to
solve it on your own. If you get stuck, you can always refer to the solution
provided in the solution review.
Good Luck!
class Rectangle:
def __init__(self):
pass
def area(self):
pass
def perimeter(self):
pass
This review provides a detailed analysis to solve the 'Implement the Rectangle Class using the Concepts of
Encapsulation' challenge.
• Solution
• Explanation
Solution #
class Rectangle:
def __init__(self, length, width):
self.__length = length
self.__width = width
def area(self):
return (self.__length * self.__width)
def perimeter(self):
return (2 * (self.__length + self.__width))
obj1 = Rectangle(4, 5)
print("Area is", obj1.area())
print("Perimeter is", obj1.perimeter())
Explanation #
1. In lines 3-4, we defined the initializer for the class and declared private
properties, __length and __width in it.
2. In line 7, we defined the method area() and returned the product of the
two properties, __length and __width , in it.
3. In line 10, we defined the method perimeter() and returned twice the
sum of the two properties, __length and __width , in it.
4. In the main function at line 13, we have defined a Rectangle class object,
obj1 with properties 4 and 5.
5. In lines 14-15, we call the methods area() and perimeter() and printed
their values.
Challenge 2: Implement the Complete Student Class
• Problem Statement
• Task
• Input
• Output
• Coding Exercise
Problem Statement #
Implement the complete Student class by completing the tasks below
Task #
Implement the following properties as private:
name
RollNumber
#
Include the following methods to get and set the private properties above:
getName()
setName()
getRollNumber()
setRollNumber()
Input #
Checking all the properties and methods.
Output #
Expecting perfectly defined fields and getter/setters.
Note: Do not use initializers to initialize the properties. Use the set
methods to do so. If the setter is not defined properly, the corresponding
getter will also generate an error even if the getter is defined properly.
Coding Exercise #
First, take a close look and design a step-by-step algorithm before trying the
implementation. This problem is designed for your practice, so initially try to
solve it on your own. If you get stuck, you can always refer to the solution
provided in the solution review.
Good luck!
class Student:
def setName(self):
pass
def getName(self):
pass
def setRollNumber(self):
pass
def getRollNumber(self):
pass
This lesson provides a solution review for the 'Implement the Complete Student Class' challenge.
• Solution
• Explanation
Solution #
class Student:
def setName(self, name):
self.__name = name
def getName(self):
return self.__name
def getRollNumber(self):
return self.__RollNumber
demo1 = Student()
demo1.setName("Alex")
print("Name:", demo1.getName())
demo1.setRollNumber(3789)
print("Roll Number:", demo1.getRollNumber())
Explanation #
We have implemented the Student class which has two private
properties: __name and __RollNumber which are initialized in the set
methods.
In line 3, we implemented setName(name) , a method which sets the
__name property.
In this lesson, you will be introduced to Inheritance, a powerful concept in Object-Oriented Programming.
• De nition
• The IS A Relationship
• The Python Object class
Now that you are familiar with the concepts of objects and classes, let’s discuss
inheritance which is another key concept in Object-Oriented Programming.
De nition #
Inheritance provides a way to create a new class from an existing class. The
new class is a specialized version of the existing class such that it inherits all
the non-private fields (variables) and methods of the existing class. The
existing class is used as a starting point or as a base to create the new class.
The IS A Relationship #
After reading the above definition, the next question that comes to your mind
is when do we use inheritance? Well, the answer is that wherever we come
across an IS A relationship between objects, we can use inheritance.
IS A IS A IS A
Shape Programming Language Vehicle
Square IS A shape
Python IS A programming language
Car IS A vehicle
So, from the above descriptions regarding inheritance, we can conclude that
we can build new classes by extending existing classes.
Shape Square
Vehicle Car
IS A IS A IS A
Corners Syntax Steering
Are things getting interesting? So, let’s move to the next lesson in which we
will discuss the syntax and terminologies related to inheritance.
The Syntax and Terminologies
In this lesson, you will learn how to use inheritance syntactically and the terminologies related to it.
• The Terminologies
• Syntax
• Example
• Explanation
The Terminologies #
In inheritance, in order to create a new class based on an existing class we use
the following terminology:
Parent Class (Super Class or Base Class): This class allows the re-use of
its public properties in another class.
Child Class (Sub Class or Derived Class): This class is the one that
inherits or extends the superclass.
Syntax #
In Python, to implement inheritance, the syntax is quite similar to the basic
class definition. The syntax is given below:
class ParentClass:
# attributes of the parent class
class ChildClass(ParentClass):
# attributes of the child class
Name of the parent class is written in brackets after the name of the child class
and this is followed by the body of the child class.
Example #
Let’s take an example of a Vehicle class as the parent class and implement a
Car class that will extend from this Vehicle class. As a Car IS A Vehicle, hence
the implementation of inheritance relation between these classes will stand
valid.
class Vehicle:
def __init__(self, make, color, model):
self.make = make
self.color = color
self.model = model
def printDetails(self):
print("Manufacturer:", self.make)
print("Color:", self.color)
print("Model:", self.model)
class Car(Vehicle):
def __init__(self, make, color, model, doors):
# calling the constructor from parent class
Vehicle.__init__(self, make, color, model)
self.doors = doors
def printCarDetails(self):
self.printDetails()
print("Doors:", self.doors)
Explanation #
In the code above, we have defined a parent class, Vehicle , in line 1 and
a child class, Car , in line 13.
Car inherits all the properties and methods of the Vehicle class and can
access and modify them.
For example in line 20 of the Car class, we have called the
printDetails() method, which was actually defined in the Vehicle class,
In this lesson, you'll get to know about the uses of the super function in Python.
super() super
def display(self):
# accessing fuelCap from the Vehicle class using super()
print("Fuel cap from the Vehicle Class:", super().fuelCap)
rst second
class ParentClass():
def __init__(self, a, b):
self.a = a
self.b = b
class ChildClass(ParentClass):
def __init__(self, a, b, c):
super().__init__(a, b)
self.c = c
obj = ChildClass(1, 2, 3)
print(obj.a)
print(obj.b)
print(obj.c)
As you can see in both the code tabs, swapping the order of line 9 and line 10
does not change the functionality of the code. This allows the user to
manipulate parameters before passing them into the parent class method.
Now let’s use the example in the previous lesson and use super() to refer to
the parent class:
without_super with_super
class Vehicle:
def __init__(self, make, color, model):
self.make = make
self.color = color
self.model = model
def printDetails(self):
print("Manufacturer:", self.make)
print("Color:", self.color)
print("Model:", self.model)
class Car(Vehicle):
def __init__(self, make, color, model, doors):
Vehicle.__init__(self, make, color, model)
self.doors = doors
def printCarDetails(self):
self.printDetails()
print("Name:", self.doors)
As you can see in the above codes, line 15 is interchangeable and produces the
same output but using super() makes the code more manageable.
So this was pretty much all about the super() function. In the next lesson, we
will discuss the different types of inheritance.
Types of Inheritance
In this lesson, you will learn about the various types of inheritance in Python.
• Single Inheritance
• Multi-level Inheritance
• Hierarchical Inheritance
• Multiple Inheritance
• Hybrid Inheritance
Based upon parent classes and child classes, there are the following five types
of inheritance:
1. Single
2. Multi-level
3. Hierarchical
4. Multiple
5. Hybrid
Single Inheritance #
In single inheritance, there is only a single class extending from another class.
We can take the example of the Vehicle class, as the parent class, and the Car
class, as the child class. Let’s implement these classes below:
Vehicle
Car
Multi-level Inheritance #
When a class is derived from a class which itself is derived from another class,
it’s called Multilevel Inheritance. We can extend the classes to as many levels
as we want to.
Vehicle
Car
Hybrid
A Car IS A Vehicle
A Hybrid IS A Car
def openTrunk(self):
print("Trunk is now open.")
Hierarchical Inheritance #
When more than one class inherits from the same class, it’s referred to as
hierarchical inheritance. In hierarchical inheritance, more than one class
extends, as per the requirement of the design, from the same base class. The
common attributes of these child classes are implemented inside the base
class.
Example:
A Car IS A Vehicle
A Truck IS A Vehicle
Vehicle
Car Truck
Multiple Inheritance #
When a class is derived from more than one base class, i.e., when a class has
more than one immediate parent class, it is called Multiple Inheritance.
Example:
HybridEngine IS A ElectricEngine .
Combustion Electric
Engine Engine
Hybrid
Engine
car = HybirdEngine()
car.setChargeCapacity("250 W")
car.setTankCapacity("20 Litres")
car.printDetails()
Hybrid Inheritance #
A type of inheritance which is a combination of Multiple and Multi-level
inheritance is called hybrid inheritance.
CombustionEngine IS A Engine .
ElectricEngine IS A Engine .
Engine
Combustion Electric
Engine Engine
Hybrid
Engine
car = HybirdEngine()
car.setPower("2000 CC")
car.setChargeCapacity("250 W")
car.setTankCapacity("20 Litres")
car.printDetails()
This lesson was about different types of inheritance. In the next lesson, we’ll
discuss the advantages of inheritance.
Advantages of Inheritance
In this lesson, you'll get to know about the advantages of using Inheritance.
• Re-usability
• Code Modi cation
• Extensibility
• Data Hiding
Re-usability #
Inheritance makes the code reusable. Consider that you are up for designing a
banking system using classes. Your model might have these:
BankAccount
holdersName;
accountBalance;
accountNumber;
getBalance()
getDetails
withdraw()
deposit()
SavingsAccount(BankAccount) CheckingAccount(BankAccount)
SavingsAccount CheckingAccount
interestAmount linkedAtmCard
addInterest() deductFee()
Ctrl+C Ctrl+V
Extensibility #
Using inheritance, one can extend the base class as per the requirements of
the derived class. It provides an easy way to upgrade or enhance specific parts
of a product without changing the core attributes. An existing class can act as
a base class from which a new class with upgraded features can be derived.
In the example above, you realize at a later point that you have to diversify
this banking application by adding another class for MoneyMarketAccount . So,
rather than implementing this class from scratch you can extend it from the
existing BankAccount class as a starting point. You can also reuse its attributes
that are common with MoneyMarketAccount .
child
class
parent
class
Data Hiding #
The base class can keep some data private so that the derived class cannot
alter it. This concept, encapsulation, has already been discussed in the
previous chapter.
BankAccount
__holdersName;
__accountBalance;
__accountNumber; } Not Accessible to
Outside world
}
getBalance()
getDetails Accessible to
withdraw() Outside world
deposit()
So these were some of the key advantages of inheritance. In the next lesson,
attempt a quick quiz to test your understanding of the concepts related to
inheritance.
Quick Quiz!
Let's test your understanding of inheritance with the help of this short quiz.
1
Which of the following syntax is used to inherit ParentClass by the
ChildClass ?
COMPLETED 0%
1 of 5
Challenge 1: Implement a Banking Account
In this challenge, you will implement a Banking Account using the concepts of inheritance.
• Problem Statement
• Task 1
• Task 2
• Task 3
• Coding Exercise
Problem Statement #
Implement a basic structure of a parent class, Account , and a child class,
SavingsAccount .
Task 1 #
Implement properties as instance variables and set them to None or 0.
title
balance
interestRate
Task 2 #
Create an initializer for Account class. The order of parameters should be the
following:
Account("Mark", 5000)
where Mark is the title and 5000 is the account balance.
Task 3 #
Implement properties as instance variables and set them to None or 0.
Account("Mark", 5000, 5)
where Mark is the title and 5000 is the balance and 5 is the interestRate .
Account
title
balance
SavingsAccount
interestRate
Coding Exercise #
First, take a close look and design a step-by-step algorithm before trying the
implementation. This problem is designed for your practice, so initially try to
solve it on your own. If you get stuck, you can always refer to the solution
provided in the solution review.
Good luck!
class Account:
def __init__(self):
# write your code here
pass
class SavingsAccount():
def __init__(self):
# write your code here
pass
This review provides a detailed analysis to solve the 'Implement a Banking Account' challenge.
• Solution
• Explanation
Solution #
class Account:
def __init__(self, title=None, balance=0):
self.title = title
self.balance = balance
class SavingsAccount(Account):
def __init__(self, title=None, balance=0, interestRate=0):
super().__init__(title, balance)
self.interestRate = interestRate
Explanation #
In lines 2-4, we have defined two instance variables, title and balance
in the Account class set them to None and 0 respectively.
In lines 8-10, we then defined the initializer for the SavingsAccount class
and called the parent class initializer using super() .
In this challenge, you will de ne methods for handling a bank account using concepts of inheritance.
• Problem Statement
• Task 1
• Task 2
• Task 3
• Task 4
• Coding Exercise
Problem Statement #
In this challenge, we will be extending the previous challenge and
implementing methods in the parent class and its corresponding child class.
The initializers for both classes have been defined for you.
Task 1 #
In the Account class, implement getBalance() method that returns balance .
Task 2 #
In the Account class, implement deposit(amount) method that adds amount to
the balance . It does not return anything.
Sample Input
balance = 2000
deposit(500)
getbalance()
Sample Output
2500
Task 3 #
In the Account class, implement withdrawal(amount) method that subtracts the
amount from the balance . It does not return anything.
Sample Input
balance = 2000
withdrawal(500)
getbalance()
Sample Output
1500
Task 4 #
In the SavingsAccount class, implement a interestAmount() method that
returns the interest amount of the current balance. Below is the formula for
calculating the interest amount:
Sample Input
balance = 2000
interestRate = 5
interestAmount()
Sample Output
100
The following figure shows what the result should logically look like:
Account
title
balance
withdrawal(amount)
deposit(amount)
getBalance()
SavingsAccount
interestRate
interestAmount()
Coding Exercise #
First, take a close look and design a step-by-step algorithm before jumping to
the implementation. This problem is designed for your practice, so initially try
to solve it on your own. If you get stuck, you can always refer to the solution
provided in the solution review.
Good luck!
Note: A new SavingsClass object is initialized at the end of the code and
test results will base on it.
class Account:
def __init__(self, title=None, balance=0):
self.title = title
self.balance = balance
def getBalance(self):
# write code here
pass
class SavingsAccount(Account):
def __init__(self, title=None, balance=0, interestRate=0):
super().__init__(title, balance)
self.interestRate = interestRate
def interestAmount(self):
# write code here
pass
This review provides a detailed analysis to solve the 'Handling a Bank Account' challenge.
• Solution
• Explanation
• Methods in the Account Class
• Methods in the SavingsAccount Class
Solution #
class Account: # parent class
def __init__(self, title=None, balance=0):
self.title = title
self.balance = balance
# withdrawal method subtracts the amount from the balance
def getBalance(self):
return self.balance
class SavingsAccount(Account):
def __init__(self, title=None, balance=0, interestRate=0):
super().__init__(title, balance)
self.interestRate = interestRate
# computes interest amount using the interest rate
def interestAmount(self):
return (self.balance * self.interestRate / 100)
Explanation #
In each of the two classes, the initializers have already been defined for you.
In line 15, we have defined the getBalance() method that returns the
value of balance.
In this lesson, the concept of Polymorphism will be explained which is an important concept in OOP.
• De nition
• A Brief Introduction
• Make Things Simpler with Polymorphism
• What does Polymorphism Achieve?
De nition #
In programming, polymorphism
refers to the same object exhibiting
different forms and behaviors.
Shape
For example, take the Shape Class.
The exact shape you choose can be
anything. It can be a rectangle, a
Rectangle Circle Polygon Diamond
circle, a polygon or a diamond. So,
these are all shapes, but their Many Shapes
A Brief Introduction #
Assume there is a parent class named Shape from which the child classes
Rectangle , Circle , Polygon , and Diamond are derived.
Suppose your application might need methods to calculate the area of each
specific shape. Of course, the area for each shape is calculated differently. So,
you can’t have a single implementation. You could throw in separate methods
in each class called, for instance, getSquareArea() , getCircleArea() etc. But
this makes it harder to remember each method’s name. Wouldn’t it be nice if
all specific area calculation methods could be called getArea() ?
Consider that the Shape class has a method called getArea(), which is
inherited by all subclasses mentioned. With polymorphism, each subclass may
have its own way of implementing the method. So, for example, when the
getArea() method is called on an object of the Rectangle class, the method
will respond by displaying the area of the rectangle. On the other hand, when
the same method is called on an object of the Circle class, the circle’s area
might be calculated and displayed on the screen.
So far, we’ve learned what polymorphism is. In the next lesson, we will learn
how to implement polymorphism in OOP.
Polymorphism Using Methods
• Example
• Explanation
Example #
class Rectangle():
# initializer
def __init__(self, width=0, height=0):
self.width = width
self.height = height
self.sides = 4
Explanation #
In the main function, at line 25, we have declared a list that has two
objects in it.
The first object is a Rectangle with width 6 and height 10, and the second
object is a Circle of radius 7.
Both the classes have the method getArea() , on lines 10 and 21, but the
execution of this method is different for each class and this is how we
have achieved polymorphism.
Method calls on lines 27 and 30 look identical, but different methods are
called. Thus, we have achieved polymorphism.
This was one way of achieving polymorphism. In the next lesson, we will
implement polymorphism using a more efficient and commonly used
approach: polymorphism using inheritance.
Polymorphism Using Inheritance
• Example
• Implementation
• Shape Class
• Rectangle Class
• Circle Class
• Complete Program
• Explanation
So far, we have learned that we can add new data and methods to a class
through inheritance. But what if we want our derived class to inherit a
method from the base class and have a different implementation for it? That
is when polymorphism, a fundamental concept in the OOP paradigm, comes
into play.
Example #
Implementation #
We will be implementing the parent class first, followed by the child classes.
Shape Class #
The Shape class has only one public method called getArea() .
class Shape:
def __init__(self)
self.sides = 0
def getArea(self):
pass
Rectangle Class #
Let’s look at the implementation of the Rectangle class:
The Rectangle class is extended from the Shape class. It inherits the sides
property from the Shape class and defines new properties, width and height .
The method getArea() returns the area of the rectangle.
Circle Class #
Let’s look at the implementation of the Circle class:
The Circle class is extended from the Shape class. It inherits the sides
property from the Shapes class and defines only one new property, radius .
The method getArea() returns the area of the circle.
Complete Program #
Now, by merging all the classes and calling the getArea() method, see what
happens:
class Shape:
def __init__(self): # initializing sides of all shapes to 0
self.sides = 0
def getArea(self):
pass
In the main function, we have declared a list that has two objects in it. The
first object is a Rectangle with width 6 and height 10 . The second object is a
Circle of radius 7.
The getArea() method returns the area of the respective shape. This is
Polymorphism; having specialized implementations of the same methods for
each class.
In the next lesson, we’ll be learning about the process of method overriding.
Method Overriding
In this lesson, you'll be learning about what method overriding is and how to achieve it in Python.
• A Brief Introduction
• Advantages and Key Features of Method Overriding
A Brief Introduction #
In this case:
class Shape:
def __init__(self): # initializing sides of all shapes to 0
self.sides = 0
def getArea(self):
pass
For any method, a child class can use the implementation in the parent
class or make its own implementation.
Now that we are familiar with the concept of method overriding, let’s
understand the operator overloading in the next lesson.
Operator Overloading
Run the code below for the implementation of the + operator for integers and
strings:
z =x + iy
complex real
imaginary
number part
part
When we add a complex number, the real part is added to the real part and
the imaginary part is added to the imaginary part.
a = 3 + 7i
b = 2 + 5i
a + b = (3 + 2) + (7 + 5)i = 5 + 12i
a − b = (3 − 2) + (7 − 5)i = 1 + 2i
Now let’s implement the complex number class and overload the + and -
operators below:
class Com:
def __init__(self, real=0, imag=0):
self.real = real
self.imag = imag
obj1 = Com(3, 7)
obj2 = Com(2, 5)
Explanation #
In the above code, we have overloaded the built-in method __add__ (line
6) and __sub__ (line 10) that are invoked when the + and the -
operators are used.
Whenever two objects of class Com are added using the + operator, the
overloaded __add__ method is called.
This method adds the real property separately and the imag property
separately and then returns a new Com class object that is initialized by
these sums.
Note that __add__ and __sub__ methods have two input parameters. The
first one is self , which we know is the reference to the class itself. The
second parameter is other . other is a reference to the other objects that
are interacting with the class object.
In line 18, obj2 will be considered the other object, the operator will be
called on the obj1 object and the returned object will be stored in obj3 .
In line 19, obj2 will be considered the other object, the operator will be
called on the obj1 object and the returned object will be stored in obj4 .
Other has Com class attributes and thus, it has the real and the imag
properties.
obj1 obj2
3 2
7
+ 5
(2+3) (5+7)
5
real
imag 12
obj3
Similarly, whenever two objects of class Com are subtracted using the -
operator, the overloaded __sub__ method is called. This method subtracts the
real property separately and the imag property separately and then returns
a new Com class object that is initialized by these differences.
Operator Method
It is for the user to decide how they want the objects to interact when an
operator operates on them, but they usually make sure that these operations
make sense. For example, the + is not going to be used for finding products of
different properties of a class.
quack
quack
?
1 of 4
swims
2 of 4
eats like a
duck
3 of 4
? is a DUCK!
4 of 4
Dynamic Typing #
Duck typing extends the concept of dynamic typing in Python.
Dynamic typing means we can change the type of an object later in the
code.
Due to the dynamic nature of Python, duck typing allows the user to use any
object that provides the required behavior without the constraint that it has to
be a subclass. See the code below for a better understanding of dynamic
typing in Python:
x = 5 # type of x is an integer
print(type(x))
class Dog:
def Speak(self):
print("Woof woof")
class Cat:
def Speak(self):
print("Meow meow")
class AnimalSound:
def Sound(self, animal):
animal.Speak()
sound = AnimalSound()
dog = Dog()
cat = Cat()
sound.Sound(dog)
sound.Sound(cat)
Explanation #
In line 13, the type of animal is not defined in the definition of the
method Sound .
Conclusion #
Now coming back to why it is called Duck typing. So, if a bird speaks like a
duck, swims like a duck, and eats like a duck; that bird is a duck.
Similarly, in the above example, animal object does not matter in the
definition of the Sound method as long as it has the associated behavior,
speak() , defined in the object’s class definition. In layman terms, since both
the animals, dog and cats, can speak like animals, they both are animals. This
is how we have achieved polymorphism without inheritance.
In the next lesson, we will discuss how to use abstract base classes in Python.
Abstract Base Classes
In this lesson, we will learn what abstract base classes are in Python and how to use them.
Abstract base classes define a set of methods and properties that a class
must implement in order to be considered a duck-type instance of that
class.
class Square(Shape):
def __init__(self, length):
self.length = length
def area(self):
return (self.length * self.length)
def perimeter(self):
return (4 * self.length)
shape = Shape()
square = Square(4)
In the example above, you can see that an instance of Shape can be created
even though an object from this class cannot stand on its own. Square class,
which is the child class of Shape , actually implements the methods, area()
and perimeter() , of the Shape class. Shape class should provide a blueprint
for its child classes to implement methods in it. To prevent the user from
making a Shape class object, we use abstract base classes.
Syntax #
To define an abstract base class, we use the abc module. The abstract base
class is inherited from the built-in ABC class. We have to use the decorator
@abstractmethod above the method that we want to declare as an abstract
method.
class ParentClass(ABC):
@abstractmethod
def method(self)
Example #
Below is a code implementation for abstract base classes:
Shape Square
@abstractmethod
def perimeter(self):
pass
class Square(Shape):
def __init__(self, length):
self.length = length
shape = Shape()
# this will code will not compile since Shape has abstract methods without
# method definitions in it
As you can see above, the code does not compile since we have not defined the
abstract methods, area and perimeter , inside the parent class, Shape , or the
child class, Square . Let’s do it and see what happens:
Shape Square
@abstractmethod
def perimeter(self):
pass
class Square(Shape):
def __init__(self, length):
self.length = length
def area(self):
return (self.length * self.length)
def perimeter(self):
return (4 * self.length)
shape = Shape()
# this will code will not compile since Shape has abstract methods without
# method definitions in it
Explanation #
Now when we define the methods, area and perimeter in the child class
Square , an object of Shape cannot be instantiated, but an object of Square
can be.
We allow the user to have a free hand over the definition of the methods
while also making sure that the methods are defined.
By using abstract base classes, we can control the classes whose objects can or
cannot be created.
That is all for polymorphism in Python. Let’s test your knowledge with a quick
quiz!
Quick Quiz!
Let's test your understanding of the concept of polymorphism with the help of a short quiz.
1
What is the output of the following piece of code?
class Parent:
def prn(self):
print("Parent")
class Child(Parent):
def __init__(self):
self.a = Parent()
def prn(self):
print("Child")
temp = Child()
temp.a.prn()
COMPLETED 0%
1 of 5
Challenge 1: Override a Method Using the Super
Function
• Problem Statement
• Sample Input
• Sample Output
• Coding Exercise
Problem Statement #
When a method in a derived class overrides a method in a base class, it is still
possible to call the overridden method using the super() function.
If you write super().method() , it will call the method that was defined in
the superclass.
You are given a partially completed code in the editor. Modify the code so that
the code returns the following:
Sample Input #
circle = XShape("Circle");
circle.getName()
Sample Output #
"Shape, Circle"
The Shape class is already prepended in the code and it has one property:
sname and one method: getName() . getName() returns sname .
Shape
sname="Shape"
getName()
XShape
xsname
getName()
Show Hint
Coding Exercise #
First, take a close look and design a step-by-step algorithm before trying the
implementation. This problem is designed for your practice, so initially try to
solve it on your own. If you get stuck, you can always refer to the solution
provided in the solution review.
Good luck!
class XShape(Shape):
# initializer
def __init__(self, name):
self.xsname = name
This review provides a detailed analysis to solve the 'Override a Method using the Super Function' challenge.
• Solution
• Explanation
Solution #
# Parent Class
class Shape:
sname = "Shape"
def getName(self):
return self.sname
# child class
class XShape(Shape):
# initializer
def __init__(self, name):
self.xsname = name
circle = XShape("Circle")
print(circle.getName())
Explanation #
Line 15: The super() function is used to call the parent class method
getName() . With the help of the super() function, the getName() method
returns the parent class sname , and which then gets appended with the
derived class xsname before returning.
Challenge 2: Implement an Animal Class
In this lesson, you will implement an animal class and its subclasses from scratch.
• Problem Statement
• Input
• Sample Input
• Sample Output
Problem Statement #
The code below has:
Inside it define:
name
sound
__init__()
Animal_details() function:
It prints the name and sound of the Animal .
Dog class
has a property family
has an initializer that calls the parent class initializer in it
through super()
has an overridden method named Animal_details() which
prints detail of the dog.
Sheep class
has a property color
Input #
name of Dog is set to Pongo, sound is set to Woof Woof and family is set
to Carnivore in the initializer of Dog object.
name of Sheep is set to Billy, sound is set to Baaa Baaa and color is set to
White in the initializer of Sheep object.
Sample Input #
Sample Output #
Name: Pongo
Sound: Woof Woof
Family: Husky
Name: Billy
Sound: Baa Baa
Color: White
problem solution
class Animal:
pass
# write your class here
class Dog(Animal):
pass
# write your class here
class Sheep(Animal):
pass
# write your class here
This lesson provides a solution review for the `Implement an Animal Class` challenge.
• Solution
• Explanation
Solution #
solution
class Animal:
def __init__(self, name, sound):
self.name = name
self.sound = sound
def Animal_details(self):
print("Name:", self.name)
print("Sound:", self.sound)
class Dog(Animal):
def __init__(self, name, sound, family):
super().__init__(name, sound)
self.family = family
def Animal_details(self):
super().Animal_details()
print("Family:", self.family)
class Sheep(Animal):
def __init__(self, name, sound, color):
super().__init__(name, sound)
self.color = color
def Animal_details(self):
super().Animal_details()
print("Color:", self.color)
Explanation #
We have implemented an Animal class which has name and sound
properties, and a method Animal_details() which is overridden in its
child classes.
Then, we implemented the Dog and Sheep classes, which are inherited
from the Animal class.
Created and initialized Dog and Sheep objects and printed their traits by
calling their respective methods.
This is it for polymorphism. In the next chapter, we will learn about the
different relationships between classes.
A Brief Introduction
The next step for us is to use different class objects to create the design of an
application. This means that independent class objects would have to find a
way to interact with each other.
Application
B
C
A
Interlinking
between
different classes
D E
Part-of Class A
Has-a
A contains a
reference to This is a slightly less concrete
B's object
relationship between two classes.
Class A and class B have a has-a
Class A
Object of relationship if one or both need the
Class B
other’s object to perform an
The object of class exists independent of A.
operation, but both class objects can
exist independently of each other.
Since Python compiles from top to bottom, make sure you have defined
the class before creating an object of that class.
Association #
In object-oriented programming, association is the common term for both the
has-a and part-of relationships but is not limited to these. When we say that
two objects are in an association relationship, this is a generic statement
which means that we don’t worry about the lifetime dependency between the
objects.
In the next couple of lessons, we will dive into the specialized forms of
association: aggregation and composition.
In the next lesson, you will learn about a technique for relating objects in
Python: aggregation.
Aggregation
In this lesson, you'll get familiar with a new way of linking different classes.
• Independent Lifetimes
• Example
Independent Lifetimes #
In aggregation, the lifetime of the owned object does not depend on the
lifetime of the owner.
The owner object could get deleted, but the owned object can continue to exist
in the program. In aggregation, the parent only contains a reference to the
child, which removes the child’s dependency.
Owned
object
Owner
class
Aggregation
You can probably guess from the illustration above that we’ll need object
references to implement aggregation.
Example #
Let’s take the example of people and their country of origin. Each person is
associated with a country, but the country can exist without that person:
class Country:
def __init__(self, name=None, population=0):
self.name = name
self.population = population
def printDetails(self):
print("Country Name:", self.name)
print("Country Population", self.population)
class Person:
def __init__(self, name, country):
self.name = name
self.country = country
def printDetails(self):
print("Person Name:", self.name)
self.country.printDetails()
c = Country("Wales", 1500)
p = Person("Joe", c)
p.printDetails()
As we can see, the Country object, c , lives on even after we delete the Person
object, p . This creates a weaker relationship between the two classes.
In the next lesson, you will learn about another technique for relating objects
in Python: composition.
Composition
• Example
• Implementation
Example #
A car is composed of an engine, tires, and doors. In this case, a Car owned
these objects, so a Car is an Owner class and tires , doors , and engine classes
are Owned classes.
Implementation #
Let’s look at the implementation of Car class for better understanding:
A car is composed of engine, tires and doors.
Engine
Door
Tire
class Engine:
def __init__(self, capacity=0):
self.capacity = capacity
def printDetails(self):
print("Engine Details:", self.capacity)
class Tires:
def __init__(self, tires=0):
self.tires = tires
def printDetails(self):
print("Number of tires:", self.tires)
class Doors:
def __init__(self, doors=0):
self.doors = doors
def printDetails(self):
print("Number of doors:", self.doors)
class Car:
def __init__(self, eng, tr, dr, color):
self.eObj = Engine(eng)
self.tObj = Tires(tr)
self.dObj = Doors(dr)
self.color = color
def printDetails(self):
self.eObj.printDetails()
self.tObj.printDetails()
self.dObj.printDetails()
print("Car color:", self.color)
We have created a Car class which contains the objects of Engine , Tires , and
Doors classes. Car class is responsible for their lifetime, i.e., when Car dies, so
does tire, engine, and doors too.
1
In a part-of relationship between two classes:
COMPLETED 0%
1 of 5
Challenge 1: Cars and Engines!
In this exercise, you have to perform composition between a sedan car class and its engine!
• Problem Statement
• Task 1
• Task 2
• Task 3
• Sample Input
• Sample Output
• Coding Exercise
Problem Statement #
You have to implement a Sedan class, which inherits from the Car class and
contains a SedanEngine object.
Note: You already know that in such a composition relation, the Sedan
class will be responsible for SedanEngine lifetime.
model
color
printDetails()
Sedan SedanEngine
engine
Task 1 #
Car initializer should take arguments in the order Car(model,color) .
1. model
2. color
1. printDetails() , which will print model and color of the Car object.
Task 2 #
SedanEngine class will have two methods:
Task 3 #
Sedan initializer should take arguments in the order Sedan(model,
color) .
Sample Input #
car1 = Sedan("Toyota","Grey")
car1.setStart()
car1.printDetails()
car1.setStop()
Sample Output #
After the implementation of your classes, the code below should produce the
following output
Coding Exercise #
First, take a close look and design a step-by-step algorithm before jumping to
the implementation. This problem is designed for your practice, so initially try
to solve it on your own. If you get stuck, you can always refer to the solution
provided in the solution review.
Good luck!
problem solution
class Car:
pass
# write your class definition here
class SedanEngine:
pass
# write your class definition
class Sedan(Car):
pass
# write your class definition
This lesson provides the solution to the challenge, "Cars and Engines!" with an explanation.
• Solution
• Explanation
Solution #
class Car:
def __init__(self, model, color):
self.model = model
self.color = color
def printDetails(self):
print("Model:", self.model)
print("Color:", self.color)
class SedanEngine:
def start(self):
print("Car has started.")
def stop(self):
print("Car has stopped.")
class Sedan(Car):
def __init__(self, model, color):
super().__init__(model, color)
self.engine = SedanEngine()
def setStart(self):
self.engine.start()
def setStop(self):
self.engine.stop()
Line 12-16: start() and stop() functions defined with their respective
outputs.
Line 20-22: Initializer for Sedan defined which also refers to the parent
class initializer using super() .
Line 27-28: stop() method of SedanEngine object is called to stop the car.
Challenge 2: Implementing a Sports Team!
• Problem Statement
• Task 1
• Task 2
• Task 3
• Coding Exercise
Problem Statement #
You have to implement 3 classes: School , Team , and Player , such that an
instance of a School should contain instances of Team objects. Similarly, a
Team object can contain instances of Player class.
teams = [ ] ID
players = [ ]
schoolName teamName
name
Has a name
Has a
addPlayers()
getTotalPlayersIn
getPlayers()
School()
You have to implement a School class containing a list of Team objects, and a
Team class comprising of a list of Player objects.
Task 1 #
Player class should have three properties that will be set using an
initializer:
1. ID
2. name
3. teamName
Task 2 #
Team class will have two properties that will be set using an initializer:
1. name
2. players , a list with player class objects in it.
1. addPlayer() , which will add new player objects in the players list.
Task 3 #
The School class will contain two properties that will be set using an
initializer:
1. addTeam , which will add new team objects in the teams list.
2. getTotalPlayersInSchool() method counts the total players in all of
the teams in the School and returns the count!
So, your school should have these players in their respective teams:
1 Harris Red
2 Carol Red
1 Johnny Blue
2 Sarah Blue
Coding Exercise #
First, take a close look and design a step-by-step algorithm before trying the
implementation. This problem is designed for your practice, so initially try to
solve it on your own. If you get stuck, you can always refer to the solution
provided in the solution review.
Good luck!
problem solution
# Player class
class Player:
pass
# Complete the implementation
p3 = Player("Johnny", 1, "Blue");
p4 = Player("Sarah", 2, "Blue");
red_team=Team("Red Team")
red_team.players.append(p1)
red_team.players.append(p2)
blue_team=Team("Blue Team")
blue_team.players.append(p2)
blue_team.players.append(p3)
mySchool=School("My School")
mySchool.teams.append(red_team)
mySchool.teams.append(blue_team)
This lesson provides the solution to the challenge, "Implementing a Sports Team!" with an explanation.
• Solution
Solution #
class Player:
def __init__(self, ID, name, teamName):
self.ID = ID
self.name = name
self.teamName = teamName
class Team:
def __init__(self, name):
self.name = name
self.players = []
def getNumberOfPlayers(self):
return len(self.players)
class School:
def __init__(self, name):
self.name = name
self.teams = []
def getTotalPlayersInSchool(self):
length = 0
for n in self.teams:
length = length + (n.getNumberOfPlayers())
return length
red_team.addPlayer(p1)
red_team.addPlayer(p2)
Line 9-11: Defined initializer for Team which also contains a list of
players .
Line 21-23: Defined initializer for School which also contains a list of
teams .
Line 41-42: Added two Player objects in the red_team 's players list.
Line 45-46: Added two Player objects in the blue_team 's players list.
Line 49-50: Added two Team objects in the mySchool 's teams list.
Conclusion
• Last Thoughts
• Bene ts of Learning OOP
• Where to Go from Here?
Last Thoughts #
Congratulations! We have mastered the art of object-oriented programming in
Python. The concepts introduced in this course have helped us understand the
behavior of classes and objects.
OOP greatly increases the security of our code as well. By hiding the inner
implementation, we ensure that no one can damage any sensitive
components.