0% found this document useful (0 votes)
4 views

Educative OOP in Python

This document introduces Object-Oriented Programming (OOP), covering its principles, including classes and objects, and the advantages of using them. It explains the differences between procedural programming and OOP, outlines the anatomy of objects and classes, and discusses user-defined data types. Additionally, it provides examples of popular OOP languages, particularly Python, and details the process of declaring classes and initializing objects in Python.

Uploaded by

malikabdullah.cc
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

Educative OOP in Python

This document introduces Object-Oriented Programming (OOP), covering its principles, including classes and objects, and the advantages of using them. It explains the differences between procedural programming and OOP, outlines the anatomy of objects and classes, and discusses user-defined data types. Additionally, it provides examples of popular OOP languages, particularly Python, and details the process of declaring classes and initializing objects in Python.

Uploaded by

malikabdullah.cc
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 176

A Brief Introduction

This lesson welcomes you to the world of Object Oriented Programming.

WE'LL COVER THE FOLLOWING

• 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.

Procedural programming is one programming paradigm among many.

In procedural programming, a program is divided into smaller parts called


methods. These methods are the basic entities used to construct a program.
One of the main advantages of procedural programming is code reusability.
However, the implementation of a complex real-world scenario becomes a
difficult task unwieldy.

Enter: Object-Oriented Programming #

Object-oriented programming, also referred to as OOP, is a programming


paradigm that includes, or relies, on the concept of classes and objects.

The basic entities in object-oriented programming are classes and objects.

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.

Objects in a program frequently represent real-world objects.

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.

Anatomy of Objects and Classes #


Objects may contain data in the form of fields (variables) and methods to
operate on that data.

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?”

Well, the answer to the above question is classes.

A Class can be thought of as a blueprint for creating objects.

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

turnOn( ) method called


turnOff( )

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.

User-De ned Data Types #


It can be inferred from the discussion above that classes are user-defined data
types implemented using primitive data types, e.g. boolean , int , char etc.
While primitive data types only focus on modeling the state of the object,
user-defined data types can encapsulate state and its behaviors into a unit.

onOff

)
n( e
r nO fals
tu or
turnOf true
f( )

Bulb boolean
User-defined Primitive
data type data type

In the next lesson, we’ll discuss some different Object-Oriented Programming


languages, Python amongst them.
Modern Object-Oriented Languages

This lesson is about the most popular Object-Oriented Programming languages and how convenient Python is.

WE'LL COVER THE FOLLOWING

• 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.

Comparing Code in Multiple Languages #


Suppose we want to write a simple function that adds two numbers and
returns their sum. We’ll write this function in four commonly used languages:

Python C++ C# Java

def add(p1, p2):


sum = p1 + p2
return sum

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.

Hence the choice of language is programmer and problem dependent.

Java and C# have emerged as the dominant object-oriented languages, but


Python is now asked for at a huge number of jobs, in addition to being a
primary building block in academics.

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.

Nonetheless, the object-oriented paradigm is a well-established programming


practice and this course is designed to make you familiar with it in one of the
most user-friendly languages, Python.
Introduction to Objects and Classes

This lesson introduces you to the concept of objects and classes and the bene ts of using them.

WE'LL COVER THE FOLLOWING

• 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

The following actions or behaviors can be performed on an employee:

Calculation of tax on salary


Calculation of salary per day
Attributes Behaviors
ID Tax on Salary
Salary Salary per day
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.

Objects and Classes #


Suppose, at Educative there are two employees: Mark and Chris. The
properties of Mark and Chris are given in the image below:

ID: 72655
Salary: 3000
Department: Software
Mark

ID: 72644
Salary: 4000
Department: Marketing
Chris
Properties
ID
salary
department

Methods

tax()
salaryPerDay()

employee class

You might see attributes referred to as properties or members by


different people. For consistency, we’ll be using the convention of
properties throughout the course.

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.

You might see behaviors referred to as member functions, or methods


by different people. For consistency, we’ll be using the convention of
methods throughout the course.

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.

Bene ts of Objects and Classes #


The concepts of objects and classes allow us to create complex applications in
Python. This is why classes and objects are considered the basic building

blocks behind all OOP’s principles.

They are also instrumental for compartmentalizing code. Different


components can become separate classes which would interact through
interfaces. These ready-made components will also be available for use in
future applications.

The use of classes makes it easier to maintain the different parts of an


application since it is easier to make changes in classes (more on this
later).

In the next lesson, we will discuss how to declare a class in Python.


Declaring a Class in Python

WE'LL COVER THE FOLLOWING

• 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:

1. Must start with a letter or underscore.


2. Should only be comprised of numbers, letters, or underscores.

Creating a Class Object #


The name of the class, MyClass , will be used to instantiate an object of the
class in our main program. We can create an object of a class by simply using
the name of the class followed by a pair of parenthesis. It looks similar to
calling a function, but Python can distinguish between the two and creates a
new object of the corresponding class. An example of this is given below:

class MyClass:
pass

obj = MyClass() # creating a MyClass Object


print(obj)

Well done! You’ve created your first object-oriented program in Python.


Printing this object, obj , will show the memory address at which this object is
stored.

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.

WE'LL COVER THE FOLLOWING

• Implementation of the Employee Class


• Accessing Properties and Assigning Values
• Creating Properties Outside a Class

Building on the concepts of the previous lesson, we will implement a class,


Employee , in Python.

Implementation of the Employee Class #


Let’s implement the Employee class illustrated below. We’ll start with just
adding the properties of the class and will later extend it by adding methods
in it.

Properties
ID
salary
department

Methods

Employee class

With_None Without_None

# this code will compile


class Employee:
# defining the properties and assigning them none
ID = None

salary = None
department = None

We’ve defined three properties as class variables; ID , salary and department ,


for the class Employee . We will discuss the concept of class variables later. For
now, focus on the syntax.

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.

Accessing Properties and Assigning Values #


To access properties of an object, the dot notation is used:

object.property

There are two ways to assign values to properties of a class.

1. Assign values when defining the class.


2. Assign values in the main code.

initializing assigning

class Employee:
# defining the properties and assigning values to them
ID = 3789
salary = 2500
department = "Human Resources"

# cerating an object of the Employee class


Steve = Employee()

# printing properties of Steve - an object of the Employee class


print("ID =", Steve.ID)
print("Salary", Steve.salary)
print("Department:", Steve.department)

Creating Properties Outside a Class #


Python, being a particularly user-friendly language, provides the user with a
feature that most languages usually do not. That is, creating properties of an
object outside the class. Let’s see an example of this by extending the example
of the Employee class we discussed above:

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

# cerating an object of the Employee class


Steve = Employee()

# assigning values to properties of Steve - an object of the Employee class


Steve.ID = 3789
Steve.salary = 2500
Steve.department = "Human Resources"
# creating a new attribute for Steve
Steve.title = "Manager"

# Printing properties of Steve


print("ID =", Steve.ID)
print("Salary", Steve.salary)
print("Department:", Steve.department)
print("Title:", Steve.title)
This is a basic implementation of the Python class with only properties. In the
next lesson, we will learn how to initialize objects in Python.
Initializing Objects

In this lesson, the world of initializers is explored. You'll learn why they are necessary for a class.

WE'LL COVER THE FOLLOWING

• 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 initialization method is similar to other methods but has a pre-defined


name, __init__ .

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

# creating an object of the Employee class with default parameters


Steve = Employee(3789, 2500, "Human Resources")

# Printing properties of Steve


print("ID :", Steve.ID)
print("Salary :", Steve.salary)
print("Department :", Steve.department)

The initializer is automatically called when an object of the class is created.


Now that we will be using initializers to make objects, a good practice would
be to initialize all of the object properties when defining the initializer.

It is important to define the initializer with complete parameters to


avoid any errors. Similar to methods, initializers also have the provision
for optional parameters.

Initializer with Optional Parameters #


Similar to methods, we can also define initializers with optional parameters.
As discussed previously, it’s necessary to assign initial values to class
properties when defining the class. So, when defining an initializer with
optional parameters, it’s essential to assign default values to the properties.

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

# creating an object of the Employee class with default parameters


Steve = Employee()
Mark = Employee("3789", 2500, "Human Resources")

# Printing properties of Steve and Mark


print("Steve")
print("ID :", Steve.ID)
print("Salary :", Steve.salary)
print("Department :", Steve.department)
print("Mark")
print("ID :", Mark.ID)
print("Salary :", Mark.salary)
print("Department :", Mark.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.

WE'LL COVER THE FOLLOWING

• De nitions
• Class Variables
• Instance Variables
• De ning Class Variables and Instance Variables
• Wrong Use of Class Variables
• Using Class Variables Smartly
• Explanation

In Python, properties can be defined into two parts:

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.

De ning Class Variables and Instance Variables #


Class variables are defined outside the initializer and instance variables are
defined inside the initializer.

class Player:
teamName = 'Liverpool' # class variables

def __init__(self, name):


self.name = name # creating instance 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)

In line 2, we have created a class variable and in line 5, we have created an


instance variable.

Wrong Use of Class Variables #


It is imperative to use class variables properly since they are shared by all the
class objects and can be modified using any one of them. Below is an example
of wrongful use of class variables:

class Player:
formerTeams = [] # class variables
teamName = 'Liverpool'
def __init__(self, name):

self.name = name # creating instance variables

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

def __init__(self, name):


self.name = name # creating instance variables
self.formerTeams = []

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.

Using Class Variables Smartly #


Class variables are useful when implementing properties that should be
common and accessible to all class objects. Let’s see an example of this:

class Player:
teamName = 'Liverpool' # class variables
teamMembers = []

def __init__(self, name):


self.name = name # creating instance variables
self.formerTeams = []
self.teamMembers.append(self.name)

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.

WE'LL COVER THE FOLLOWING

• The Purpose of Methods


• De nition and Declaration
• Method Parameters
• Return Statement
• The self Argument
• Method Overloading
• Advantages of Method Overloading

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.

The Purpose of Methods #


Methods act as an interface between a program and the properties of a class
in the program.

These methods can either alter the content of the properties or use their
values to perform a particular computation.

De nition and Declaration #

A method is a group of statements that performs some operations and


may or may not return a result.

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.

The return statement must be immediately followed by the return value.

The self Argument #


One of the major differences between functions and methods in Python is the
first argument in the method definition. Conventionally, this is named self .
The user can use different names as well, but self is used by almost all the
developers working in Python. We will also be using this convention for ease
of understanding.

This pseudo-variable provides a reference to the calling object, that is the


object to which the method or property belongs to. If the user does not
mention the self as the first argument, the first parameter will be treated for
reference to the object.

Note: The self argument only needs to be passed in the method


definition and not when the method is called.

Below is an example of implementing methods in a class:

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)

# initializing an object of the Employee class


Steve = Employee(3789, 2500, "Human Resources")

# Printing properties of Steve


print("ID =", Steve.ID)
print("Salary", Steve.salary)
print("Department:", Steve.department)
print("Tax paid by Steve:", Steve.tax())
print("Salary per day of Steve", Steve.salaryPerDay())
Method Overloading #

Overloading refers to making a method perform different operations


based on the nature of its arguments.

Unlike in other programming languages, methods cannot be explicitly


overloaded in Python but can be implicitly overloaded.

In order to include optional arguments, we assign default values to those


arguments rather than creating a duplicate method with the same name. If
the user chooses not to assign a value to the optional parameter, a default
value will automatically be assigned to the variable.

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 tax(self, title=None):


return (self.salary * 0.2)

def salaryPerDay(self):
return (self.salary / 30)

# cerating an object of the Employee class


Steve = Employee()

# Printing properties of Steve


print("Demo 1")
Steve.demo(1, 2, 3)
print("\n")

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.

If we redefine a method several times and give it different arguments, Python


uses the latest method definition for its implementation.

Advantages of Method Overloading #


One might wonder that we could simply create new methods to perform
different jobs rather than overloading the same method. However, under the
hood, overloading saves us memory in the system. Creating new methods is
costlier compared to overloading a single one.

Since they are memory-efficient, overloaded methods are compiled faster


compared to different methods, especially if the list of methods is long.

An obvious benefit is that the code becomes simple and clean. We don’t have
to keep track of different methods.

Polymorphism is a very important concept in object-oriented programming. It


will come up later on in the course, but method overloading plays a vital role
in its implementation.
Increases
execution
speed

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

In this lesson, we will be discussing class methods and static methods

WE'LL COVER THE FOLLOWING

• Methods in a Python Class


• Class Methods
• Syntax
• Static Methods
• Syntax

Methods in a Python Class #


In Python classes, we have three types of methods: instance methods, class
methods, and static methods. In the previous lesson, we discussed instance
methods. In this lesson, we will be focusing on 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

Below is a code implementation for class methods:

class Player:
teamName = 'Liverpool' # class variables

def __init__(self, name):


self.name = name # creating instance 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")

Below is a code implementation for static methods:

class Player:
teamName = 'Liverpool' # class variables

def __init__(self, name):


self.name = name # creating instance 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.

WE'LL COVER THE FOLLOWING

• Public Attributes
• Private Attributes
• Private Properties
• Code Explanation
• Private Methods
• Code Explanation
• Accessing Private Attributes in the Main Code
• Not So Protected

In Python, we can impose access restrictions on different data members and


member functions. The restrictions are specified through access modifiers.
Access modifiers are tags we can associate with each member to define which
parts of the program can access it directly.

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.

Technically in Python, all methods and properties in a class are publicly


available by default. If we want to suggest that a method should not be used
publicly, we have to declare it as private explicitly.
Below is an example to implement public attributes:

class Employee:
def __init__(self, ID, salary):
# all properties are public
self.ID = ID
self.salary = salary

def displayID(self):
print("ID:", self.ID)

Steve = Employee(3789, 2500)


Steve.displayID()
print(Steve.salary)

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.

Access granted! Calling salary


Employee property in the
Employee 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

Steve = Employee(3789, 2500)


print("ID:", Steve.ID)
print("Salary:", Steve.__salary) # this will cause an error

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.

When it is tried to be accessed outside the class, the following error is


generated:

'Employee' object has no attribute '__salary'

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

def displaySalary(self): # displaySalary is a public method


print("Salary:", self.__salary)

def __displayID(self): # displayID is a private method


print("ID:", self.ID)

Steve = Employee(3789, 2500)


Steve.displaySalary()
Steve.__displayID() # this will generate an error

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.

displaySalary() method is a public method, so it can be accessed from


outside the class. This method can access the private property __salary
as well.

__displayID() method is a private method, so it cannot be accessed from


outside the class.

When displayID() tried to be accessed from outside the class, the


following error is generated:

'Employee' object has no attribute '__displayID()'

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.

Accessing Private Attributes in the Main Code #


As discussed above, it is not common to have private variables in Python.

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

absolutely necessary to access a private property or a method, they can


access it using the _<ClassName> prefix for the property or method. An
example of this is shown below:

class Employee:
def __init__(self, ID, salary):
self.ID = ID
self.__salary = salary # salary is a private property

Steve = Employee(3789, 2500)


print(Steve._Employee__salary) # accessing 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.

Now, let’s test your knowledge with a quick quiz!


Quick Quiz!

A short quiz to test your understanding of classes in Python.

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.

WE'LL COVER THE FOLLOWING

• 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

Sample Method Output

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.

WE'LL COVER THE FOLLOWING

• 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 the next challenge, we will calculate the student’s performance.


Challenge 2: Calculate the Student's Performance

In this exercise, you have to calculate the student's total marks using the concept of Classes.

WE'LL COVER THE FOLLOWING

• 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.

Below is the formula for calculating the percentage:

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.

WE'LL COVER THE FOLLOWING

• 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 line 9, When have then defined the totalObtained() method. In this


method, we added individual marks of all the subjects and returned the
sum.

At last in line 12, we defined Percentage() . In this method, we called


totalObtained() and used its value to calculate the percentage. 300 is the
total number of marks for a student.

In the next challenge, we’ll solve another problem of implementing a


calculator class.
Challenge 3: Implement a Calculator Class

In this exercise, you have to implement a calculator which can perform addition, subtraction, multiplication, and
division.

WE'LL COVER THE FOLLOWING

• 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 #

Implement an initializer to initialize the values of num1 and num2 .

Properties #
num1

num2

Task 2 #
Methods #

add() , a method which returns the sum of num1 and num2 .

subtract() , a method which returns the subtraction of num1 from num2 .

multiply() , a method which returns the product of num1 and num2 .

divide() , a method which returns the division of num2 by num1 .

Input #
Pass numbers (integers or floats) in the initializer.

Output #
addition, subtraction, division, and multiplication

Sample Input #

obj = Calculator(10, 94);


obj.add()
obj.subtract()
obj.multiply()
obj.divide()

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

The solution will be explained in the next lesson.


Solution Review 3: Implement a Calculator Class

This review provides a detailed analysis to solve the 'Implement a Calculator Class' challenge.

WE'LL COVER THE FOLLOWING

• 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)

demo1 = Calculator(10, 94)


print("Addition:", demo1.add())
print("Subtraction:", demo1.subtract())
print("Mutliplication:", demo1.multiply())
print("Division:", demo1.divide())

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 line 7, we implemented add() , a method that returns the sum, num1 +


num1 , of both properties.

In line 10, we implemented subtraction() , a method that returns the


subtraction of num1 from num2 .

In line 13, we wmplemented multiplication() , a method that returns the


product, num2 × num1 , of both properties.

In line 16, we implemented division() , a method that returns the


division of num2 by num1 .
What is Information Hiding?

In this lesson, you will get familiar with a very important aspect of Object Oriented Programming called data
hiding.

WE'LL COVER THE FOLLOWING

• 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.

Information hiding refers to the concept of hiding the inner workings


of a class and simply providing an interface through which the outside
world can interact with the class without knowing what’s going on
inside.

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.

A patient visits the


hospital due to an
ailment

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

The transaction shown above seems relatively straightforward. Data hiding is


useful because it can apply the same simplicity to transactions between
objects of different classes.

Components of Data Hiding #


Data hiding can be divided into two primary components:

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.

WE'LL COVER THE FOLLOWING

• De nition
• Advantages of Encapsulation

De nition #
Encapsulation is a fundamental programming technique used to achieve data
hiding in OOP.

Encapsulation in OOP refers to binding the data and the methods to


manipulate that data together in a single unit, that is, class.

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

When encapsulating classes, a good convention is to declare all variables of a


class private . This will restrict direct access by the code outside that class.
At this point, a question can be raised that if the methods and variables are
encapsulated in a class, then “how can they be used outside of that 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

In this lesson, we will learn about getters and setters in OOP.

WE'LL COVER THE FOLLOWING

• Get and Set


• Example

Get and Set #


In order to allow controlled access to properties from outside the class, getter
and setter methods are used.

A getter method allows reading a property’s value.

A setter method allows modifying a property’s value.

It is a common convention to write the name of the corresponding member


fields with the get or set command.
getProperty() value

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 setUsername(self, x):


self.__username = x

def getUsername(self):
return (self.__username)

Steve = User('steve1')
print('Before setting:', Steve.getUsername())
Steve.setUsername('steve2')
print('After setting:', Steve.getUsername())

In the above class, User , we have defined a private property, named


__username , which the main code cannot access. Also, note that we have
started the name of this private property with __ .

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.

Now let’s understand encapsulation using examples in our next lesson.


Understanding Encapsulation Using Examples

In this lesson, you will get a rmer understanding of encapsulation in Python with the help of examples.

WE'LL COVER THE FOLLOWING

• A Bad Example
• A Good Example
• Explanation

As discussed earlier, encapsulation refers to the concept of binding data and


the methods operating on that data in a single unit also called a class.

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.

Consider that we are up for designing an application and are working on


modeling the log in part of that application. We know that a user needs a
username and a password to log into the application.

An elementary User class will be modeled as:

Having a property userName


Having a property password
A method named login() to grant access

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()

The code for the above illustration is given below:

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!")

Steve = User("Steve", "12345")


Steve.login("steve", "12345")
Steve.login("steve", "6789")
Steve.password = "6789"
Steve.login("steve", "6789")

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

def login(self, userName, password):


if ((self.__userName.lower() == userName.lower())
and (self.__password == password)):
print(
"Access Granted against username:",
self.__userName.lower(),
"and password:",
self.__password)
else:
print("Invalid Credentials!")

# 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

If you comment out line 23, the program will work.

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!

Test your knowledge on data hiding with this theoretical 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'.

WE'LL COVER THE FOLLOWING

• 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:

Area = length × width

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:

P erimeter = 2 × (length + width)

Sample Properties

length = 4
width = 5

Sample Method Output

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

The solution will be explained in the next lesson.


Solution Review: Implement Rectangle Class Using the
Encapsulatio

This review provides a detailed analysis to solve the 'Implement the Rectangle Class using the Concepts of
Encapsulation' challenge.

WE'LL COVER THE FOLLOWING

• 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

In this challenge, you will implement a student class.

WE'LL COVER THE FOLLOWING

• 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()

Implement this class according to the rules of encapsulation.

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

The solution will be explained in the next lesson.


Solution Review: Implement the Complete Student
Class

This lesson provides a solution review for the 'Implement the Complete Student Class' challenge.

WE'LL COVER THE FOLLOWING

• Solution
• Explanation

Solution #
class Student:
def setName(self, name):
self.__name = name

def getName(self):
return self.__name

def setRollNumber(self, RollNumber):


self.__RollNumber = RollNumber

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 line 6, we implemented getName() , a method which returns the __name


property.

In line 9, we implemented getRollNumber() , a method which returns the


__RollNumber property.

In line 12, we implemented setRollNumber(RollNumber) , a method which


sets the __RollNumber property.
What is Inheritance?

In this lesson, you will be introduced to Inheritance, a powerful concept in Object-Oriented Programming.

WE'LL COVER THE FOLLOWING

• 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.

Square Python Car

IS A IS A IS A
Shape Programming Language Vehicle

In the above illustration, we can see the objects have a IS A relationship


between them. We can write it as:

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.

Existing Class Derived Class

Shape Square

Programming Language Python

Vehicle Car

Let’s find out where an IS A relationship doesn’t exist.

Square Python Car

IS A IS A IS A
Corners Syntax Steering

The above illustration shows that we cannot use inheritance whenever an IS


A relationship doesn’t exist between the classes.

The Python Object class #


The primary purpose of object-oriented programming is to enable a
programmer to model the real-world objects using a programming language.

In Python, whenever we create a class , it is, by default, a subclass of built-in


Python object class . This makes it an excellent example of inheritance in
Python. This class has very few properties and methods but does provide a
strong basis for Object-Oriented Programming in Python.

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.

WE'LL COVER THE FOLLOWING

• 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.

A child class has all public attributes of the parent class.

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)

obj1 = Car("Suzuki", "Grey", "2015", 4)


obj1.printCarDetails()

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 the printCarDetails() method.

Before implementing inheritance in depth, let’s learn another important


concept, super() , in the next lesson.
Super Function

In this lesson, you'll get to know about the uses of the super function in Python.

WE'LL COVER THE FOLLOWING

• What is the super() Function?


• Use Cases of the super() Function
• Accessing Parent Class Properties
• Calling Parent Class Methods
• Using with Initializers

What is the super() Function? #


The use of super() comes into play when we implement inheritance. It is used
in a child class to refer to the parent class without explicitly naming it. It
makes the code more manageable, and there is no need to know the name of
the parent class to access its attributes.

Note: Make sure to add parenthesis at the end to avoid a compilation


error.

super() super

Use Cases of the super() Function #


The super function is used in three relevant contexts:
Accessing Parent Class Properties #
Consider the fields named fuelCap defined inside a Vehicle class to keep
track of the fuel capacity of a vehicle. Another class named as Car extends
from this Vehicle class. We declare a class property inside the Car class with
the same name, i.e., fuelCap but different value. Now, if we want to refer to
the fuelCap field of the parent class inside the child class, we will then have to
use the super() function.

Let’s understand this using the code below:

class Vehicle: # defining the parent class


fuelCap = 90

class Car(Vehicle): # defining the child class


fuelCap = 50

def display(self):
# accessing fuelCap from the Vehicle class using super()
print("Fuel cap from the Vehicle Class:", super().fuelCap)

# accessing fuelCap from the Vehicle class using self


print("Fuel cap from the Car Class:", self.fuelCap)

obj1 = Car() # creating a car object


obj1.display() # calling the Car class method display()

Calling Parent Class Methods #


Just like the properties, super() is also used with the methods. Whenever a
parent class and the immediate child class have any methods with the same
name, we use super() to access the methods from the parent class inside the
child class. Let’s go through an example:

class Vehicle: # defining the parent class


def display(self): # defining diplay method in the parent class
print("I am from the Vehicle Class")

class Car(Vehicle): # defining the child class


# defining diplay method in the parent class
def display(self):
super().display()
print("I am from the Car Class")
obj1 = Car() # creating a car object
obj1.display() # calling the Car class method printOut()

Using with Initializers #


Another essential use of the function super() is to call the initializer of the
parent class from the inside of the initializer of the child class.

Note: It is not necessary that the call to super() in a method or an


initializer is made in the first line of the method.

Below is an example of using super() in initializer inside the child class.

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)

obj1 = Car("Suzuki", "Grey", "2015", 4)


obj1.printCarDetails()

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.

WE'LL COVER THE FOLLOWING

• 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

class Vehicle: # parent class


def setTopSpeed(self, speed): # defining the set
self.topSpeed = speed
print("Top speed is set to", self.topSpeed)

class Car(Vehicle): # child class


def openTrunk(self):
print("Trunk is now open.")

corolla = Car() # creating an object of the Car class


corolla.setTopSpeed(220) # accessing methods from the parent class
corolla.openTrunk() # accessing method from its own class

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

Let’s implement the three classes illustrated above:

A Car IS A Vehicle
A Hybrid IS A Car

class Vehicle: # parent class


def setTopSpeed(self, speed): # defining the set
self.topSpeed = speed
print("Top speed is set to", self.topSpeed)
class Car(Vehicle): # child class of Vehicle

def openTrunk(self):
print("Trunk is now open.")

class Hybrid(Car): # child class of Car


def turnOnHybrid(self):
print("Hybrid mode is now switched on.")

priusPrime = Hybrid() # creating an object of the Prius class


priusPrime.setTopSpeed(220) # accessing methods from the parent class
priusPrime.openTrunk() # accessing method from the parent class
priusPrime.turnOnHybrid() # accessing method from the parent class

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

Below is a code example of hierarchal inheritance.

class Vehicle: # parent class


def setTopSpeed(self, speed): # defining the set
self.topSpeed = speed
print("Top speed is set to", self.topSpeed)
class Car(Vehicle): # child class of Vehicle
pass

class Truck(Vehicle): # child class of Car


pass

corolla = Car() # creating an object of the Prius class


corolla.setTopSpeed(220) # accessing methods from the parent class

volvo = Car() # creating an object of the Prius class


volvo.setTopSpeed(180) # accessing methods from the parent class

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 .

HybridEngine IS A CombustionEngine as well.

Combustion Electric
Engine Engine

Hybrid
Engine

Below is a code example of multiple inheritance.

class CombustionEngine(): # Child class inherited from Engine


def setTankCapacity(self, tankCapacity):
self.tankCapacity = tankCapacity

class ElectricEngine(): # Child class inherited from Engine


def setChargeCapacity(self, chargeCapacity):
self.chargeCapacity = chargeCapacity

# Child class inherited from CombustionEngine and ElectricEngine


class HybirdEngine(CombustionEngine, ElectricEngine):
def printDetails(self):
print("Tank Capacity:", self.tankCapacity)
print("Charge Capacity:", self.chargeCapacity)

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 .

HybridEngine IS A ElectricEngine and a CombustionEngine .

Engine

Combustion Electric
Engine Engine

Hybrid
Engine

Below is the code implementation of an example of Hybrid inheritance.

class Engine: # Parent class


def setPower(self, power):
self.power = power

class CombustionEngine(Engine): # Child class inherited from Engine


def setTankCapacity(self, tankCapacity):
self.tankCapacity = tankCapacity

class ElectricEngine(Engine): # Child class inherited from Engine


def setChargeCapacity(self, chargeCapacity):
self.chargeCapacity = chargeCapacity

# Child class inherited from CombustionEngine and ElectricEngine

class HybirdEngine(CombustionEngine, ElectricEngine):


def printDetails(self):
print("Power:", self.power)
print("Tank Capacity:", self.tankCapacity)
print("Charge Capacity:", self.chargeCapacity)

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.

WE'LL COVER THE FOLLOWING

• 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:

A parent class: BankAccount


A child class: SavingsAccount
Another child class: CheckingAccount

BankAccount

holdersName;
accountBalance;
accountNumber;

getBalance()
getDetails
withdraw()
deposit()

SavingsAccount(BankAccount) CheckingAccount(BankAccount)

SavingsAccount CheckingAccount

interestAmount linkedAtmCard
addInterest() deductFee()

#rest of the #rest of the


attributes are attributes are
inherited without inherited without
any implementation any implementation
here here
In the above example, you don’t need to duplicate the code for the deposit()
and withdraw() methods inside the child classes, namely SavingsAccount and
CheckingAccount . In this way, you can avoid the duplication of code.

Ctrl+C Ctrl+V

Code Modi cation #


Suppose you put the same code in different classes all over the place, but what
happens when you have to make a change to a function? You have to make
changes in several places. Chances are, you forget some places and bugs are
introduced. With inheritance, all changes will be localized, and
inconsistencies will be avoided.

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

Child class can add on to the 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.

WE'LL COVER THE FOLLOWING

• 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.

Account has the following properties:

title

balance

SavingsAccount has the following properties:

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.

Create an initializer for SavingsAccount class using the initializer of the


Account class in the order below:

Account("Mark", 5000, 5)

where Mark is the title and 5000 is the balance and 5 is the interestRate .

Account
title
balance

SavingsAccount
interestRate

Based and Derived Classes Structure

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

The solution will be explained in the next lesson.


Solution Review: Implement a Banking Account

This review provides a detailed analysis to solve the 'Implement a Banking Account' challenge.

WE'LL COVER THE FOLLOWING

• 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 line 7, we have defined SavingsAccount as a child class of Account .

In lines 8-10, we then defined the initializer for the SavingsAccount class
and called the parent class initializer using super() .

We have defined one instance variable, interestRate , in the


SavingsAccount class and set it to None in the initializer.
Challenge 2: Handling a Bank Account

In this challenge, you will de ne methods for handling a bank account using concepts of inheritance.

WE'LL COVER THE FOLLOWING

• 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:

interest = interest rate x balance


amount 100

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()

Based and Derived Classes Structure

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 withdrawal(self, amount):


# write code here
pass

def deposit(self, amount):

# write code here


pass

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

# code to test - do not edit this


demo1 = SavingsAccount("Mark", 2000, 5) # initializing a SavingsAccount object

The solution will be explained in the next lesson.


Solution Review: Handling a Bank Account

This review provides a detailed analysis to solve the 'Handling a Bank Account' challenge.

WE'LL COVER THE FOLLOWING

• 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 withdrawal(self, amount):


self.balance = self.balance - amount
# deposit method adds the amount to the balance

def deposit(self, amount):


self.balance = self.balance + amount
# this method just returns the value of 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)

obj1 = SavingsAccount("Steve", 5000, 10)


print("Initial Balance:", obj1.getBalance())
obj1.withdrawal(1000)
print("Balance after withdrawal:", obj1.getBalance())
obj1.deposit(500)

print("Balance after deposit:", obj1.getBalance())


print("Interest on current balance:", obj1.interestAmount())

Explanation #
In each of the two classes, the initializers have already been defined for you.

Methods in the Account Class #


In line 7, we have defined the withdrawal(amount) method. It takes a
number, amount as an input parameter and subtracts it from the balance .

In line 11, we have defined the deposit(amount) method. It takes a


number, amount , as an input parameter and subtracts it to the balance .

In line 15, we have defined the getBalance() method that returns the
value of balance.

Methods in the SavingsAccount Class #


We have defined the interestAmount() method that returns the amount of
interest depending on the value of the interestRate defined at the time
of the creation of the object.
What is Polymorphism?

In this lesson, the concept of Polymorphism will be explained which is an important concept in OOP.

WE'LL COVER THE FOLLOWING

• De nition
• A Brief Introduction
• Make Things Simpler with Polymorphism
• What does Polymorphism Achieve?

De nition #

The word Polymorphism is a combination of two Greek words, Poly


meaning many and Morph meaning forms.

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

properties are different. This is called


Polymorphism.

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() ?

Make Things Simpler with Polymorphism #


You would only have to remember one method name and when you call that
method, the method specific to that object would be called. This can be
achieved in object-oriented programming using polymorphism. The base class
declares a function getArea() without providing an implementation. Each
derived class inherits the function declaration and can provide its own
implementation

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.

What does Polymorphism Achieve? #


In effect, polymorphism cuts down the work of the developer. When the time
comes to create more specific subclasses with certain unique attributes and
behaviors, the developer can alter the code in the particular portions where
the responses differ. All other pieces of the code can be left untouched.

So far, we’ve learned what polymorphism is. In the next lesson, we will learn
how to implement polymorphism in OOP.
Polymorphism Using Methods

In this lesson, we will implement polymorphism using methods.

WE'LL COVER THE FOLLOWING

• Example
• Explanation

We have learned how polymorphism is useful in making code manageable. In


this lesson, we will learn how to implement polymorphism using methods. In
the next lesson, we will implement it using inheritance.

Example #

Here, we consider two shapes


Rectangle Circle
that are defined as classes:
Rectangle and Circle. These getArea() getArea()

classes contain the getArea() width


radius
length
method which calculates the sides sides

area for the respective shape


depending on the values of
their properties.

class Rectangle():

# initializer
def __init__(self, width=0, height=0):
self.width = width
self.height = height
self.sides = 4

# method to calculate Area2


def getArea(self):
return (self.width * self.height)
class Circle():
# initializer
def __init__(self, radius=0):
self.radius = radius
self.sides = 0

# method to calculate Area


def getArea(self):
return (self.radius * self.radius * 3.142)

shapes = [Rectangle(6, 10), Circle(7)]


print("Sides of a rectangle are", str(shapes[0].sides))
print("Area of rectangle is:", str(shapes[0].getArea()))

print("Sides of a circle are", str(shapes[1].sides))


print("Area of circle is:", str(shapes[1].getArea()))

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

In this lesson, we will be implementing polymorphism using the OOP concepts.

WE'LL COVER THE FOLLOWING

• 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 #

Here, we consider the example of a


Shape
Shape class, which is the base class
getArea()
while many shapes like Rectangle
and Circle extending from the base sides
inherits inherits
class are derived classes. These
derived classes inherit the getArea() Rectangle Circle

method, and provide a shape-specific getArea() getArea()

implementation, which calculates its


width radius
area. length
Rectangle and Circle inherit from Shape

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() .

Let’s look at the implementation of the Shape class:

class Shape:
def __init__(self)
self.sides = 0

def getArea(self):
pass

Rectangle Class #
Let’s look at the implementation of the Rectangle class:

# Rectangle IS A Shape with a specific width and height


class Rectangle(Shape): # derived form Shape class
# initializer
def __init__(self, width, height):
self.width = width
self.height = height
self.sides = 4

# method to calculate Area


def getArea(self):
return (self.width * self.height)

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:

# Circle IS A Shape with a specific radius


class Circle(Shape): # derived form Shape class
# initializer
def __init__(self, radius):
self.radius = radius
self.sides = 0

# method to calculate Area


def getArea(self):
return (self.radius * self.radius * 3.142)

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

class Rectangle(Shape): # derived form Shape class


# initializer
def __init__(self, width=0, height=0):
self.width = width
self.height = height
self.sides = 4

# method to calculate Area


def getArea(self):
return (self.width * self.height)

class Circle(Shape): # derived form Shape class


# initializer
def __init__(self, radius=0):
self.radius = radius

# method to calculate Area


def getArea(self):
return (self.radius * self.radius * 3.142)

shapes = [Rectangle(6, 10), Circle(7)]


print("Area of rectangle is:", str(shapes[0].getArea()))
print("Area of circle is:", str(shapes[1].getArea()))
Explanation #

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.

WE'LL COVER THE FOLLOWING

• A Brief Introduction
• Advantages and Key Features of Method Overriding

A Brief Introduction #

Method overriding is the process of redefining a parent class’s method in


a subclass.

In other words, if a subclass provides Shape


a specific implementation of a
method that had already been
getArea()

defined in one of its parent classes, it


is known as method overriding.
Rectangle Circle
In the previous example, the
Rectangle and Circle classes were
getArea() getArea()
overriding the getArea() method
from the Shape class.

In this case:

The method in the parent class is Overrides the getArea()


Method
called overridden method.
The methods in the child classes
are called overriding methods.
We have already seen the
implementation of the getArea()

method in the previous lesson, which


depicts the concept of overriding. The
highlighted portions show where
method overriding is happening.

Let’s have a look!

class Shape:
def __init__(self): # initializing sides of all shapes to 0
self.sides = 0

def getArea(self):
pass

class Rectangle(Shape): # derived form Shape class


# initializer
def __init__(self, width=0, height=0):
self.width = width
self.height = height
self.sides = 4

# method to calculate Area


def getArea(self):
return (self.width * self.height)

class Circle(Shape): # derived form Shape class


# initializer
def __init__(self, radius=0):
self.radius = radius

# method to calculate Area


def getArea(self):
return (self.radius * self.radius * 3.142)

shapes = [Rectangle(6, 10), Circle(7)]


print("Area of rectangle is:", str(shapes[0].getArea()))
print("Area of circle is:", str(shapes[1].getArea()))

Advantages and Key Features of Method


Overriding #
The derived classes can give their own specific implementations to
inherited methods without modifying the parent class methods.

For any method, a child class can use the implementation in the parent
class or make its own implementation.

Method Overriding needs inheritance and there should be at least one


derived class to implement it.

The method in the derived classes usually have a different


implementation from one another.

Now that we are familiar with the concept of method overriding, let’s
understand the operator overloading in the next lesson.
Operator Overloading

In this lesson, we will be learning about operator overloading in Python.

WE'LL COVER THE FOLLOWING

• Overloading Operators in Python


• Overloading Operators for a User-De ned Class
• Explanation
• Special Functions for Some Common Operators

Overloading Operators in Python #


Operators in Python can be overloaded to operate in a certain user-defined
way. Whenever an operator is used in Python, its corresponding method is
invoked to perform its predefined function. For example, when the + operator
is called, it invokes the special function, __add__ , in Python, but this operator
acts differently for different data types. For example, the + operator adds the
numbers when it is used between two int data types and merges two strings
when it used between string data types.

Run the code below for the implementation of the + operator for integers and
strings:

print(5 + 3) # adding integers using '+'


print("money" + "maker") # merging strings using '+'

Overloading Operators for a User-De ned Class


#
When a class is defined, its objects can interact with each other through the
operators, BUT it is necessary to define the behavior of these operators
through operator overloading.
We are going to implement a class that represents a complex number, which
has a real part and an imaginary part.

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.

Similarly, when we subtract a complex number, the real part is subtracted


from the real part and the imaginary part is subtracted from the imaginary
part.

An example of this is shown below:

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

def __add__(self, other): # overloading the `+` operator


temp = Com(self.real + other.real, self.imag + other.imag)
return temp

def __sub__(self, other): # overloading the `-` operator


temp = Com(self.real - other.real, self.imag - other.imag)
return temp

obj1 = Com(3, 7)
obj2 = Com(2, 5)

obj3 = obj1 + obj2

obj4 = obj1 - obj2

print("real of obj3:", obj3.real)


print("imag of obj3:", obj3.imag)
print("real of obj4:", obj4.real)
print("imag of obj3:", obj4.imag)

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

You can name the second argument to be anything, but as per


convention, we will be using the word other to reference the other
object.

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.

Special Functions for Some Common Operators #


Below are some common special functions that can be overloaded while
implementing operators for objects of a class.

Operator Method

+ __add__ (self, other)


- __sub__ (self, other)

/ __truediv__ (self, other)

* __mul__ (self, other)

< __lt__ (self, other)

> __gt__ (self, other)

== __eq__ (self, other)

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.

This is it for operator overloading in Python. Now let’s learn how to


implement polymorphism using duck typing in Python.
Polymorphism Using Duck Typing

In this lesson, we will be implementing polymorphism using duck typing.

WE'LL COVER THE FOLLOWING

• What is Duck Typing?


• Dynamic Typing
• Implementing Duck Typing
• Explanation
• Conclusion

Duck typing is one of the most useful concepts in Object-Oriented


Programming in Python. Using duck typing, we can implement polymorphism
without using inheritance. The name may sound a bit funny right now, but we
will come to understand it at the end of the lesson!

What is Duck Typing? #


We say that if an object quacks like a duck, swims like a duck, eats like a duck
or in short, acts like a duck, that object is a duck.

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))

x = "Educative" # type x is now string


print(type(x))

Implementing Duck Typing #


Let us see an example to implement this:

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 .

Type of animal is determined when the method is called, so it does not


matter which object type you are passing as a parameter in the Sound()
method, what matters is that the Speak() method should be defined in
all the classes whose objects are passed in the Sound() method.

We can use any property or method of animal in the AnimalSound class as


long as it is declared in that class.

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.

WE'LL COVER THE FOLLOWING

• What are Abstract Base Classes?


• Why use Abstract Base Classes?
• Syntax
• Example
• Explanation

What are Abstract Base Classes? #


Duck typing is useful as it simplifies the code and the user can implement the
functions without worrying about the data type. But this may not be the case
all the time. The user might not follow the instructions to implement the
necessary steps for duck typing. To cater to this issue, Python introduced the
concept of Abstract Base Classes, or ABC.

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.

Why use Abstract Base Classes? #


Let’s see an example below to understand why we should use abstract base
classes:

class Shape: # Shape is a child class of ABC


def area(self):
pass
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()
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.

from abc import ABC, abstractmethod

class ParentClass(ABC):

@abstractmethod
def method(self)

Example #
Below is a code implementation for abstract base classes:

Shape Square

from abc import ABC, abstractmethod

class Shape(ABC): # Shape is a child class of ABC


@abstractmethod
def area(self):
pass

@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

from abc import ABC, abstractmethod

class Shape(ABC): # Shape is a child class of ABC


@abstractmethod
def area(self):
pass

@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.

Note: Methods with @abstractmethod decorators must be defined either


in the parent class or the child class.

By using abstract base classes, we can control the classes whose objects can or
cannot be created.

Abstract methods must be defined in child classes for proper


implementation of inheritance.

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

In this challenge, you will override a method using super().

WE'LL COVER THE FOLLOWING

• 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()

Parent and child class structures

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

def getName(self): # overriden method


return (self.xsname)
The solution will be explained in the next lesson.
Solution Review 1: Override a Method using the Super
Function

This review provides a detailed analysis to solve the 'Override a Method using the Super Function' challenge.

WE'LL COVER THE FOLLOWING

• 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

def getName(self): # overriden method


return (super().getName() + ", " + self.xsname)

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.

WE'LL COVER THE FOLLOWING

• Problem Statement
• Input
• Sample Input
• Sample Output

Problem Statement #
The code below has:

A parent class named Animal .

Inside it define:
name

sound

__init__()

Animal_details() function:
It prints the name and sound of the Animal .

Then there are two derived classes

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

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 sheep.

The derived classes should override the Animal_details() method


defined in the Animal class.

The overridden method in Dog class should print the value of


family as well as the name and sound .

The overridden method in Sheep class should print the value of


color as well as the name and sound

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.

Now, call Animal_Details() from their respective objects.

Here’s a sample result which you should get.

Sample Input #

d = Dog("Pongo", "Woof Woof", "Husky")


d.Animal_details()
print(" ")
s = Sheep("Billy", "Baaa Baaa", "White")
s.Animal_details()

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

print("Write your solution in the classes above.")

Solution will be reviewed in the next lesson.


Solution Review 2: Implement an Animal Class

This lesson provides a solution review for the `Implement an Animal Class` challenge.

WE'LL COVER THE FOLLOWING

• 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)

d = Dog("Pongo", "Woof Woof", "Husky")


d.Animal_details()
print("")
s = Sheep("Billy", "Baa Baa", "White")
s.Animal_details()

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.

Dog has an additional property, family , and the overridden method,


Animals_details() . This method calls the parent method using the
super() function and also prints the family property.

Sheep has an additional property, color , and the overridden method,


Animals_details() . This method calls the parent method using the
super() function and also prints the color property.

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

In this lesson, we will learn about different relationships between classes.

WE'LL COVER THE FOLLOWING

• Interaction Between Class Objects


• Relationships Between Classes
• Association

Interaction Between Class Objects #


By now, we’ve learned all we need to know about the definition and the
behavior of a class. The concepts of inheritance and polymorphism taught
us how to create dependent classes out of a base class. While inheritance
represents a relationship between classes, there are situations where there
are relationships between objects.

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

Relationships Between Classes #


There are three main class relationships we need to know. We have studied
the IS A relation in the Inheritance chapter. We’ll study the other two below:

Part-of Class A

In this relationship, one class object is


a component of another class object. Class B Class C
Object Object
Given two classes, class A and class B,
they are in a part-of relation if a class
A object is a part of class B object, or
vice-versa. Class A contains objects of Class B and C.

An instance of the component class


can only be created inside the main
class. In the example to the right,
class B and class C have their own
implementations, but their objects
are only created once a class A object
is created. Hence, part-of is a
dependent relationship.

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.

This implies that a class has-a


reference to an object of the other
class but does not decide the lifetime
of the other class’s referenced object.

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.

WE'LL COVER THE FOLLOWING

• Independent Lifetimes
• Example

Aggregation follows the Has-A model. This creates a parent-child relationship


between two classes, with one class owning the object of another.

So, what makes aggregation unique?

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

The owner (parent)


simply points to the
owned object (child)

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()

# deletes the object p


del p
print("")
c.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

In this lesson, you'll learn how to achieve composition in Python.

WE'LL COVER THE FOLLOWING

• Example
• Implementation

Composition is the practice of accessing other class objects in your class. In


such a scenario, the class which creates the object of the other class is known
as the owner and is responsible for the lifetime of that object.

Composition relationships are Part-of relationships where the part must


constitute a segment of the whole object. We can achieve composition by
adding smaller parts of other classes to make a complex unit.

So, what makes composition so unique?

In composition, the lifetime of the owned object depends on the lifetime


of the owner.

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)

car = Car(1600, 4, 2, "Grey")


car.printDetails()

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.

Now, let’s test your knowledge with a quick quiz!


Quick Quiz!

Test your knowledge of composition, aggregation, and association in this quiz!

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!

WE'LL COVER THE FOLLOWING

• 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.

Consider this diagram for reference:


Car

model
color

printDetails()

Sedan SedanEngine

engine

setStart() Part-of start()


stop()
setStop()

Task 1 #
Car initializer should take arguments in the order Car(model,color) .

Car class should have two properties:

1. model
2. color

Car class should have one method:

1. printDetails() , which will print model and color of the Car object.

Task 2 #
SedanEngine class will have two methods:

1. start() , which will print:

Car has started.

1. stop() , which will print:


Car has stopped.

Task 3 #
Sedan initializer should take arguments in the order Sedan(model,
color) .

Sedan class will have one property:

1. engine , which is a SedanEngine class object that should be created


when the object is initialized.

Sedan class will have two methods:

1. setStart() , which will call the start() method of SedanEngine .

2. setStop() , which will call the stop() method of SedanEngine .

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

Car has started.


Model: Toyota
Color: Grey
Car has stopped.

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

# code to test your implementation


# uncomment this code when you want to test your implementation
'''
car1 = Sedan("Toyota", "Grey")
car1.setStart()
car1.printDetails()
car1.setStop()
'''
print("Complete the challenge.")

The solution is explained in the next lesson!


Solution Review 1: Cars and Engines!

This lesson provides the solution to the challenge, "Cars and Engines!" with an explanation.

WE'LL COVER THE FOLLOWING

• 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()

car1 = Sedan("Toyota", "Grey")


car1.setStart()
car1.printDetails()
car1.setStop()
Explanation #
Line 2-4: Initialized car properties

Line 6-8: printDetails() prints properties of Car .

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() .

Created an object of SedanEngine and assigned it to the Sedan class


property engine .

Line 24-25: start() method of SedanEngine object is called to start the


car.

Line 27-28: stop() method of SedanEngine object is called to stop the car.
Challenge 2: Implementing a Sports Team!

In this exercise, you have to perform aggregation between 3 classes.

WE'LL COVER THE FOLLOWING

• 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.

Consider this diagram for clarification:

School Team Player

teams = [ ] ID
players = [ ]
schoolName teamName
name
Has a name
Has a
addPlayers()
getTotalPlayersIn
getPlayers()
School()

School, Team, Player: Class Representation

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.

It will have two methods:

1. addPlayer() , which will add new player objects in the players list.

2. getNumberOfPlayers() , which will return the total number of players


in the players list.

Task 3 #
The School class will contain two properties that will be set using an
initializer:

1. teams , a list of team class objects


2. name

It will have two methods:

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:

Player ID’s Player Names 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

# Team class contains a list of Player


# Objects
class Team:
pass

# Complete the implementation

# School class contains a list of Team


# objects.
class School:
pass

# Complete the implementation

# code to test the implementation


# remove backticks when you want to test the implemenation of your code
'''
p1 = Player("Harris", 1, "Red");
p2 = Player("Carol", 2, "Red");

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)

print("Total players in my school:", mySchool.getTotalPlayersInSchool())


'''
print("Complete the challenge.")

The solution is explained in the next lesson!


Solution Review 2: Implementing a Sports Team!

This lesson provides the solution to the challenge, "Implementing a Sports Team!" with an explanation.

WE'LL COVER THE FOLLOWING

• 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)

def addPlayer(self, player):


self.players.append(player)

class School:
def __init__(self, name):
self.name = name
self.teams = []

def addTeam(self, team):


self.teams.append(team)

def getTotalPlayersInSchool(self):
length = 0
for n in self.teams:
length = length + (n.getNumberOfPlayers())
return length

p1 = Player(1, "Harris", "Red")


p2 = Player(2, "Carol", "Red")
p3 = Player(1, "Johnny", "Blue")
p4 = Player(2, "Sarah", "Blue")

red_team = Team("Red Team")

red_team.addPlayer(p1)
red_team.addPlayer(p2)

blue_team = Team("Blue Team")


blue_team.addPlayer(p2)
blue_team.addPlayer(p3)

mySchool = School("My School")


mySchool.addTeam(red_team)
mySchool.addTeam(blue_team)

print("Total players in mySchool:", mySchool.getTotalPlayersInSchool())

Line 2-5: Defined initializer for Player .

Line 9-11: Defined initializer for Team which also contains a list of
players .

Line 13-14: getNumberOfPlayers() returns the length of the list, players .

Line 16-17: addPlayer() appends a Player object in the players list.

Line 21-23: Defined initializer for School which also contains a list of
teams .

Line 25-26: addTeam() appends a Team object in the players list.

Line 28-32: getTotalPlayersInSchool returns the total number of players


in the school by adding the length of all the players list in the teams list.

Line 35-38: Created four Player objects.

Line 40: Created a Team object, red_team .

Line 41-42: Added two Player objects in the red_team 's players list.

Line 44: Created a Team object, blue_team .

Line 45-46: Added two Player objects in the blue_team 's players list.

Line 48: Created a School object, mySchool .

Line 49-50: Added two Team objects in the mySchool 's teams list.
Conclusion

A few nal words before we end this course.

WE'LL COVER THE FOLLOWING

• 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.

Bene ts of Learning OOP #


Now, we can easily create simple and efficient classes that can be reused in
multiple programs. In the modern world, the ability to create convenient
interfaces between classes is a useful skill and OOP is the perfect guide for
acquiring it.

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.

Where to Go from Here? #


The logical next step is the realm of object-oriented design (OOD), but that’s a
topic for another course.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy