Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
44 views45 pages

CMPS 201 Course

This document provides an introduction to object-oriented programming in Python. It discusses key concepts like classes, objects, methods, and attributes. Classes are user-defined data types that act as blueprints for objects. Objects are instances of classes that contain specifically defined data and behaviors. Methods are functions defined within classes that describe an object's behaviors. Attributes define an object's state and properties. The document provides examples of creating classes to represent people with attributes like name, age, etc. and methods to print their details. It demonstrates how to create lists and dictionaries of class objects.

Uploaded by

abdulla.n.a.kw
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)
44 views45 pages

CMPS 201 Course

This document provides an introduction to object-oriented programming in Python. It discusses key concepts like classes, objects, methods, and attributes. Classes are user-defined data types that act as blueprints for objects. Objects are instances of classes that contain specifically defined data and behaviors. Methods are functions defined within classes that describe an object's behaviors. Attributes define an object's state and properties. The document provides examples of creating classes to represent people with attributes like name, age, etc. and methods to print their details. It demonstrates how to create lists and dictionaries of class objects.

Uploaded by

abdulla.n.a.kw
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/ 45

International University of Science & Technology in Kuwait

‫الجــامـعـة الــدولـيـة يف ال ـكـويـت‬

Part II: Object-Oriented Programming in Python


Pierre Murr, PhD

Any fool can write code that a computer can understand. Good programmers write code that
humans can understand. – Martin Fowler

Spring 2023

Page 1
Chapter 1. Introduction
Object oriented programming and other paradigms like functional programming are all about
making code easier to work with and understand. We call code that is easy to work with "clean
code".

Object-oriented programming (OOP) is a computer programming model that organizes


software design around data, or objects, rather than functions and logic. An object can be
defined as a data field that has unique attributes and behavior.

The best analogy to describe object-programming has to be Legos. Solving a problem with
object-oriented programming is a lot like building something with them. You piece them
together like you would with a Lego set. Object-oriented programming has two important
concepts: classes and objects. If we continue with our Lego analogy, classes would be the
types of Lego bricks that you can use. Each of them has its own set of properties such as color
and dimension. But the value of those properties will vary from brick to brick. For example, you
can have two of them with the same dimension, but one is green, and the other is yellow. Or
you could have two bricks of the same color, but one is a 2x2 brick, and the other is a 1x12. In
object-oriented programming, each brick would be an object. And you combine these bricks to
solve problems just like you would with Lego bricks. Except those combines to make a Lego
set.

The structure, or building blocks, of object-oriented programming include the following:


Classes are user-defined data types that act as the blueprint for individual objects, attributes
and methods.
Objects are instances of a class created with specifically defined data. Objects can correspond
to real-world objects or an abstract entity. When class is defined initially, the description is the
only object that is defined.
Methods are functions that are defined inside a class that describe the behaviors of an object.
Each method contained in class definitions starts with a reference to an instance object.
Additionally, the subroutines contained in an object are called instance methods. Programmers
use methods for reusability or keeping functionality encapsulated inside one object at a time.
Attributes are defined in the class template and represent the state of an object. Objects will
have data stored in the attributes field. Class attributes belong to the class itself.

Page 2
Chapter 2. Class, Properties, Methods and Instance
We used to use primitive data structures: numbers, strings, and lists. With those primitive data
structures, you can represent simple pieces of information, such as the first and last name of a
person, his age, civil ID number and you have two ways to store such data:

1- By using multiple variables

Python Code (2.1)


firstName1 = "Bob"
lastName1 = "Smith"
Age1 = 35
civilID1 = "288010504124"
firstName2 = "Paul"
lastName2 = "Harry"
Age2 = 20
civilID2 = "203121112345"

2- By using multiple Lists

Python Code (2.2)


Bob = ["Bob", "Smith" 35, "288010504124"]
Paul = ["Paul", "Harry" 20, "203121112345"]

In both ways, your code will be large and difficult to manage and manipulate. The better solution
is to use the Class.

Classes are used to create user-defined data structures. Classes define variables called
attributes; attributes are the data for the class. Classes as well define functions called methods,
which identify the behaviors and actions that an object created from the class can perform with
its data.

Class starts with keyword class by which is followed by the name of the class and a colon.
Any code that is indented below the class definition is considered part of the class’s body.
Here’s an example of a Person class:

Python Code (2.3)


class Person:
# class attributes
firstName = ""
lastName = ""
Age = 0
civilID = ""

Page 3
# class method
def PrintName(self):
print("Full name:" + self.firstName+' '+self.lastName)

The self parameter is a reference to the current instance of the class, and is used to access
variables that belongs to the class.

Class is the blueprint, in order to assign actual information, we must create and instance.
An instance is an object that is built from a class and contains real data. An instance of
the Person class is not a blueprint anymore. It’s an actual Person with a first name, last name,
age and civil ID number.

Python Code (2.4)


class Person:
# class attributes
firstName = ""
lastName = ""
Age = 0
civilID = ""
# class method
def PrintName(self):
print("Full Name:" + self.firstName+' '+self.lastName)

Bob = Person()
Bob.firstName = "Bob"
Bob.lastName = "Smith"
Bob.age = 35
Bob.civilID = "288010504124"
print("Bob's ",end='')
Bob.PrintName()

Paul = Person()
Paul.firstName = "Paul"
Paul.lastName = "Harry"
Paul.age = 20
Paul.civilID = "203121112345"
print("Paul's ",end='')
Paul.PrintName()

Result
Bob's Full Name:Bob Smith
Paul's Full Name:Paul Harry

Page 4
Use the pass keyword when you do not want to add any other properties or methods to the
class. For example:
class test:
pass

You can create a list of classes; it is the same list that you know but the elements are
classes:

Python Code (2.5)


class Student:
stuID = None
firstName = None
lastName = None
Age = None
civilID = None

def setData(self,sID, fName, lName,age,civilID):


self.stuID = sID
self.firstName = fName
self.lastName = lName
self.Age = age
self.civilID = civilID

def PrintName(self):
print(self.firstName+' '+self.lastName+'
'+str(self.Age)+' '+self.civilID)

classList = []
for i in range(0,3):
stuID = input("Enter the student ID:")
fName = input("Enter the first name:")
lName = input("Enter the last name:")
age = int(input("Enter the age:"))
cid = input("Enter the Civil ID:")
temp = Student()
temp.setData(stuID,fName,lName,age,cid)
classList.append(temp)

for stu in classList:


print("Student with ID:",stu.stuID, end=' ')
stu.PrintName()
Result
Enter the first name:Jhonny

Page 5
Enter the last name:Smith
Enter the age:25
Enter the Civil ID:112233
Enter the first name:Faten
Enter the last name:Angy
Enter the age:31
Enter the Civil ID:223344
Enter the first name:Tom
Enter the last name:Korsky
Enter the age:27
Enter the Civil ID:334455
Jhonny Smith 25 112233
Faten Angy 31 223344
Tom Korsky 27 334455

You can create a dictionary of classes; it is the same dictionary that you know but the
elements are classes:

Python Code (2.6)


class Student:
firstName = None
lastName = None
Age = None
civilID = None

def setData(self, fName, lName,age,civilID):


self.firstName = fName
self.lastName = lName
self.Age = age
self.civilID = civilID

def PrintName(self):
print(self.firstName+' '+self.lastName+'
'+str(self.Age)+' '+self.civilID)

classDict = {}
for i in range(0,3):
stuID = input("Enter the student ID:")
fName = input("Enter the first name:")
lName = input("Enter the last name:")
age = int(input("Enter the age:"))
cid = input("Enter the Civil ID:")
temp = Student()
temp.setData(fName,lName,age,cid)
classDict[stuID] = temp

Page 6
for stu in classDict:
print("Student with ID:",stu, end=' ')
classDict[stu].PrintName()
Result
Enter the student ID:1001
Enter the first name:Jhonny
Enter the last name:Smith
Enter the age:31
Enter the Civil ID:112233
Enter the student ID:1002
Enter the first name:Faten
Enter the last name:Angy
Enter the age:26
Enter the Civil ID:223344
Enter the student ID:1003
Enter the first name:Tom
Enter the last name:Korsky
Enter the age:34
Enter the Civil ID:445566
Student with ID: 1001 Jhonny Smith 31 112233
Student with ID: 1002 Faten Angy 26 223344
Student with ID: 1003 Tom Korsky 34 445566

Private variables/methods: The term python private variables/methods refer to those


variable/methods values that have the scope within a class. There are no 'private variables' in
Python classes as such. All variables and methods in Python are public by default. There is
sometimes an emulation of private variables by using the double underscore __ prefix to the
variable's names. This makes these variables invisible or not easily visible outside of the class
that contains them. This is achieved through name mangling. These 'private variables' are not
really secure or private because they can be accessed by using certain workaround code. So
prefixing the variable names with single underscore _ (semiprivate) and double underscore __
(fully private) makes them difficult to access and not easily visible outside the class which
contains them

Python Code (2.7)


class number:
def __init__(self,vx,vy):
self.x = vx
self.__y = vy

def sety(self,vy):
self.__y=vy

def gety(self):
return self.__y

Page 7
def __mulxy(self):
return self.x * self.__y

def mul2(self):
return self.__mulxy()

i = number(10,20)
print(i.x)
#print(i.y) #Will give error
print(i.gety())
#print(i.__mulxy()) #will give error
print(i.mul2())
print(i._number__y)
print(i._number__mulxy())

Result
10
20
200
20
200

Exercises
2.1 Modify the code (2.4), make it print the person’s age and civil ID by using a new
method
2.2 What will display the following: print(Paul.age)
2.3 Create a class animal, assign attributes and methods, create 2 instance and test
the class

Chapter 3. The __init__() and __str__() functions


When a class defines an __init__() method, class instantiation automatically
invokes __init__() for the newly created class instance. Sometimes the
__init__() method is called: “constructor”

Python Code (3.1)


class Person:
def __init__(self, fName, lName,age,civilID):

Page 8
self.firstName = fName
self.lastName = lName
self.Age = age
self.civilID = civilID
# class method
def PrintName(self):
print("Full Name:" + self.firstName+' '+self.lastName)

Bob = Person("Bob","Smith",35,"288010504124")
print("Bob's ",end='')
Bob.PrintName()
Paul = Person("Paul","Harry",20,"203121112345")
print("Paul's ",end='')
Paul.PrintName()
Result
Bob's Full Name:Bob Smith
Paul's Full Name:Paul Harry

The __init__() function does not necessarily need parameters passed to it. There can be
default parameters.

Python Code (3.2)


class Multiply:
def __init__(self, x=1,y=1):
self.x = x
self.y = y
def PrintResult(self):
print(self.x,'*',self.y,'=',self.x*self.y)

var1 = Multiply()
var1.PrintResult()
var2 = Multiply(5,3)
var2.PrintResult()
Result
1 * 1 = 1
5 * 3 = 15

The __str__() function controls what should be returned when the class object is
represented as a string.

Python Code (3.2)


class Multiply:

Page 9
def __init__(self, x=1,y=1):
self.x = x
self.y = y
def __str__(self):
return f"{str(self.x)}*{str(self.y)}={str(self.x*self.y)}"

print(Multiply(5,3))
v = Multiply(8,4)
print(v)
Result
5*3=15
8*4=32

You can delete an object by using:


del var1

This will call the function __del()__ as in the following example:

Python Code (3.3)


class test:
def __init__(self):
print("Instance created")
def __del__(self):
print("Instance deleted")

t = test()
del t
Result
Instance created
Instance deleted

Classes define functions called methods, which identify the behaviors and actions that an
object created from the class can perform with its data. Methods can be used to assign values
to attributes, retrieve and calculate necessary data.

To call a method from the class in the code (3.1) we used:


Bob.PrintName()

Where Bob is the instance and PrintName is a method in the class Person

Page 10
Exist an alternative way, store the method in a variable and use is later. For example:
PrintBob = Bob.PrintName
PrintBob()

There are some cases when we don’t know exactly the number of parameters. For example,
we need to create a class to calculate the sum of integer numbers (we don’t know how many
numbers) and in case if the first number is float, we must then calculate the average of those
numbers.

Python Code (3.4)


class sumNumbers:
def __init__(self, *args):
if isinstance(args[0], int):
self.s = 0
for i in args:
self.s += i
elif isinstance(args[0], float):
self.s = 0
for i in args:
self.s += i
self.s /= len(args)

test1 = sumNumbers(10,5,4)
print(test1.s)
test2 = sumNumbers(1,2,3,4,5,6)
print(test2.s)
test3 = sumNumbers(2.5,3.2,6.7)
print(test3.s)
Result
19
21
4.133333333333334

Chapter 4. Inheritance
Inheritance is one of the most important aspects of OOP. The main idea behind incorporating
inheritance is to make it more reusable. Cutting out code duplication allows you to shorten your
project’s production time and therefore achieve lower overall cost. The idea of using the code

Page 11
re-usability is in place of writing the same code, again and again, we can simply inherit the
properties of one class into the other.

Another benefit of using inheritance is its ability to produce more readable code. Using
inheritance means that your code repeats itself less because you only define shared functions
once, in the parent class. Having only one definition is also handy when you need to modify an
attribute or a function for both parent and child classes. You can update the function or the
attribute in the parent class, and the changes will automatically take effect for the child classes
as well.

The following example explains the importance of inheritance:


Consider a group of vehicles. You need to create classes for Bus, Car, Bike and Truck. The
methods fuelAmount(), capacity(), applyBrakes() will be the same for all three
classes. If we create these classes avoiding inheritance then we have to write all of these
functions in each of the three classes as shown below figure:

You can clearly see that the above process results in duplication of the same code 4 times.
This increases the chances of error and data redundancy. To avoid this type of situation,
inheritance is used. If we create a class Vehicle and write these three functions in it and inherit
the rest of the classes from the vehicle class, then we can simply avoid the duplication of data
and increase re-usability. Look at the below diagram in which the three classes are inherited
from vehicle class:

Page 12
And you might have more details like:

Classes Pedal Bike and Motor Bike are inherited from class Bike, same for classes Salon car,
Jeep car and Van car are inherited from class Car and classes Heavy truck and medium truck
are inherited from class Truck.

The class from which a class inherits is called the parent or superclass. A class which inherits
from a parent is called a subclass, also called heir class or child class. Parent are sometimes
called ancestors as well. There exists a hierarchical relationship between classes. It's similar
to relationships or categorizations that we know from real life. Think about vehicles, for

Page 13
example. Bikes, cars, buses and trucks are vehicles. Salon, van, jeeps cars are all cars and by
being cars they are vehicles as well. We could implement a vehicle class in Python, which
might have methods like accelerate and brake. Cars, Buses and Trucks and Bikes can be
implemented as subclasses which will inherit these methods from vehicle.

Any class can be a parent class, so the syntax is the same as creating any other class:

class A:
pass

To create a class that inherits the functionality from another class, send the parent class as a
parameter when creating the child class, for example, to create a class B that inherits class
A:

class B(A):
pass

In the following example

Python Code (4.1)


class A:
x=0
def setX(self,x):
self.x = x
def showX(self):
print("X in A:",self.x)
class B(A):
pass

v1 = B()
v1.setX(5)
v1.showX()
Result
X in A: 5

If the instance of the child class B should display X in a different way. In this case we have
to redefine the method showX() inside the class B. This is called overriding. A method of a
parent class gets overridden by redefining it with same name in the child class

Python Code (4.2)


class A:
x=0
def setX(self,x):

Page 14
self.x = x
def showX(self):
print("X in A:",self.x)
class B(A):
def showX(self):
print("But X in B:",self.x)

vA = A()
vA.setX(5)
vA.showX()

vB = B()
vB.setX(7)
vB.showX()
Result
X in A: 5
But X in B: 7

When we override a method, we sometimes want to reuse the method of the parent.

Python Code (4.3)


class A:
x=0
def setX(self,x):
self.x = x
def showX(self):
print("X=",self.x)
class B(A):
def showX(self):
A.showX(self)
print("But in B")

vA = A()
vA.setX(5)
vA.showX()

vB = B()
vB.setX(7)
vB.showX()
Result
X= 5
X= 7
But in B

Page 15
An inheritance becomes multiple inheritances when a class inherits more than one parent
class. The child class after inheriting properties from various parent classes has access to all
of their objects. In the following example, we have one Child class which is inheriting properties
of three-parent classes Parent_1, Parent_2, and Parent_3. All the classes have different
functions and all of the functions are called using the object of the Child class.

Python Code (4.4)


class Parent_1:
def f1(self):
print("Function of parent_1 class.")

class Parent_2:
def f2(self):
print("Function of parent_2 class.")

class Parent_3:
def f3(self):
print("function of parent_3 class.")

class Child(Parent_1, Parent_2, Parent_3):


def f4(self):
print("Function of child class.")

object_1 = Child()
object_1.f1()
object_1.f2()
object_1.f3()
object_1.f4()

Result
Function of parent_1 class.
Function of parent_2 class.
function of parent_3 class.
Function of child class.

Child classes can have additional attributes and methods to add additional functionalities which
do not exists in the parent class.

In some cases, child class needs to access methods and attributes in the parent class. For
example, child class needs to access the function __init__ from the parent class, in this
case we can use the function super() function that will make the child class inherit all the
methods and properties from its parent.

Page 16
Example:
Three different categories of people you'll meet at university: students,
faculty members and staff. All have first name, last name, civil id
number.
Students have additional attributes like: Major, GPA etc.
Faculty members have additional attributes as well: Rank, Major etc.
Staff have additional attributes as well: Office, Salary etc.
Write a program that creates a parent class “Person” and three child
classes for students, faculty members and staff.
Create instances for each case and display the results.
Python Code (4.5)
class Person:
def __init__(self, fname, lname,civilID):
self.firstname = fname
self.lastname = lname
self.civilID = civilID
def printPerson(self):
print(self.firstname, self.lastname,self.civilID,end='
')

class Student(Person):
def __init__(self, fname, lname, civilID,major,GPA):
super().__init__(fname, lname,civilID)
self.major = major
self.GPA = GPA
def printInfo(self):
Person.printPerson(self)
print(self.major, self.GPA)

class Faculty(Person):
def __init__(self, fname, lname, civilID,rank,major):
super().__init__(fname, lname,civilID)
self.rank = rank
self.major = major
def printInfo(self):
Person.printPerson(self)
print(self.major, self.rank)

class Staff(Person):
def __init__(self, fname, lname, civilID,Office,Salary):
super().__init__(fname, lname,civilID)
self.Office = Office
self.Salary = Salary
def printInfo(self):
Person.printPerson(self)

Page 17
print(self.Office, self.Salary)

s = Student("Mike", "Olsen", "112233", "Computer


Engineering",3.2)
s.printInfo()
f = Faculty("Tom", "Smith", "223344", "Assistant
Professor","Accounting")
f.printInfo()
x = Staff("Faten", "Korsky", "334455", "Registrar",500)
x.printInfo()

Result
Mike Olsen 112233 Computer Engineering 3.2
Tom Smith 223344 Accounting Assistant Professor
Faten Korsky 334455 Registrar 500

Understand self
With self, you are able to access the attributes and methods of the class. In the following
example, each instance will show info related to them:
Python Code (4.6)
class Fruit:
def __init__(self, name, color):
self.name = name
self.color = color
def info(self):
print(self.color, self.name)

banana = Fruit("Banana", "Yellow")


banana.info()
apple = Fruit("Apple", "Red")
apple.info()
Result
Banana Yellow
Apple Red

For the sake of clarity, let’s prove that the self indeed refers to the object itself.
To do this, let’s check the memory locations of self and a Fruit object.
In Python, you can check the memory address of an object with the id() function.

Python Code (4.7)


class Fruit:
def __init__(self):

Page 18
print("Self address =", id(self))

Banana = Fruit()
print("Banana address =", id(Banana))
print("")
Apple = Fruit()
print("Apple address =", id(Apple))
Result
Self address = 2234820575632
Banana address = 2234820575632

Self address = 2234820575344


Apple address = 2234820575344

As you can see, the memory addresses are equal. This shows you that self inside the Fruit
class points to the same address as the banana and apple objects you just created. In other
words, the self parameter really refers to the object itself.

Notice that self is not a reserved keyword in Python. You could use any name you like
instead as long as there’s one.

Python Code (4.8)


class Fruit:
def __init__(this, name, color):
this.name = name
this.color = color
def info(this):
print(this.color, this.name)
banana = Fruit("Banana", "Yellow")
apple = Fruit("Apple", "Red")
banana.info()
apple.info()

This code works the exact same way as the one that used self.
But because self is so commonly used among Python developers, it’s advisable not to use
anything else. You are unlikely to encounter anything else than self when working with
classes in Python.

The self works better with inheritance:

Python Code (4.9)


class A:

Page 19
def __init__(self, x):
self.x = x
def myfunc(self):
print("class A, x=",self.x)

class B(A):
def __init__(self, x):
self.x = x
def myfunc(self):
print("class B, x=",self.x)
def myfunc2(self):
super().myfunc()
A.myfunc(self)
def changex(self):
A.x = 50
print("self:",self.x,"in A:",A.x)

o1 = A(1)

o2 = B(20)
o2.myfunc()
o2.myfunc2()
o2.changex()
o1.myfunc())
Result
class B, x= 20
class A, x= 20
class A, x= 20
self: 20 in A: 50
class A, x= 1

and with multiple inheritance as well:


Python Code (4.10)
class A:
def __init__(self, x):
self.x = x
def myfunc(self):
print("class A, x=",self.x)

class B:
def __init__(self, y):
self.y = y
def myfunc(self):
print("class B, y=",self.y)

class C(A,B):

Page 20
def __init__(self,x,y):
A.__init__(self,x)
B.__init__(self,y)
A.myfunc(self)
B.myfunc(self)

o1 = C(10,20)
Result
class A, x= 10
class B, y= 20

Chapter 5. Polymorphism
Polymorphism is taken from the Greek words Poly (many) and morphism (forms). It means that
the same function name can be used for different types. This makes programming more
intuitive and easier. In programming, polymorphism means the same method name (but
different signatures) being used for different types. The key difference is the data types and
number of arguments used in function.

An example of polymorphism for functions, the same function sumNumbers can take 2 or 3
parameters.

Python Code (5.1)


def sumNumbers(x, y, z = 0):
return x + y + z

print(sumNumbers(2, 3))
print(sumNumbers(2, 3, 4))

Result
5
9

The below code shows how Python can use three different class types, in the same way. We
create a for loop that iterates through a tuple of objects. Then call the methods without being
concerned about which class type each object is. We assume that these methods actually exist
in each class.

Python Code (5.2)


class Kuwait():
def capital(self):
print("Kuwait is the capital of Kuwait.")

Page 21
def language(self):
print("Arabic is the official language in Kuwait.")

def location(self):
print("Kuwait is an Arabic Gulf country.")

class Lebanon():
def capital(self):
print("Beirut is the capital of Lebanon.")

def language(self):
print("Arabic is the official language in Lebanon.")

def location(self):
print("Lebanon is a MEA country.")

class France():
def capital(self):
print("Paris is the capital of France.")

def language(self):
print("French is the official language in France.")

def location(self):
print("France is an European country.")

Country1 = Kuwait()
Country2 = Lebanon()
Country3 = France()
for country in (Country1, Country2,Country3):
country.capital()
country.language()
country.location()

Result
Kuwait is the capital of Kuwait.
Arabic is the official language in Kuwait.
Kuwait is an Arabic Gulf country.
Beirut is the capital of Lebanon.
Arabic is the official language in Lebanon.
Lebanon is a MEA country.
Paris is the capital of France.
French is the official language in France.
France is a European country.

Page 22
Another form of Polymorphism is the method Overriding (See code 4.2 and 4.3), this method
lets us define methods in the child class that have the same name as the methods in the parent
class. In inheritance, the child class inherits the methods from the parent class. However, it is
possible to modify a method in a child class that it has inherited from the parent class. This is
particularly useful in cases where the method inherited from the parent class doesn’t quite fit
the child class. In such cases, we re-implement the method in the child class.

It is also possible to create a function that can take any object, allowing for polymorphism. In
this example, let’s create a function called “func()” which will take an object which we will name
“obj”. Though we are using the name ‘obj’, any instantiated object will be able to be called into
this function. Next, let’s give the function something to do that uses the ‘obj’ object we passed
to it. In this case, let’s call the three methods: capital(), language() and location(), each of which
is defined in the three classes ‘Kuwait’, ‘Lebanon’ and ‘France’. With those, we can call their
action using the same func() function:

Python Code (5.3)


class Kuwait():
def capital(self):
print("Kuwait is the capital of Kuwait.")

def language(self):
print("Arabic is the official language in Kuwait.")

def location(self):
print("Kuwait is an Arabic Gulf country.")

class Lebanon():
def capital(self):
print("Beirut is the capital of Lebanon.")

def language(self):
print("Arabic is the official language in Lebanon.")

def location(self):
print("Lebanon is a MEA country.")

class France():
def capital(self):
print("Paris is the capital of France.")

def language(self):
print("French is the official language in France.")

def location(self):
print("France is an european country.")

Page 23
def func(obj):
obj.capital()
obj.language()
obj.location()

Country1 = Kuwait()
Country2 = Lebanon()
Country3 = France()
func(Country1)
func(Country2)
func(Country3)

Result
Kuwait is the capital of Kuwait.
Arabic is the official language in Kuwait.
Kuwait is an Arabic Gulf country.
Beirut is the capital of Lebanon.
Arabic is the official language in Lebanon.
Lebanon is a MEA country.
Paris is the capital of France.
French is the official language in France.
France is a European country.

Polymorphism With Inheritance


Polymorphism is mainly used with inheritance. In inheritance, child class inherits the attributes
and methods of a parent class. The existing class is called a base class or parent class, and
the new class is called a subclass or child class or derived class.

Using method overriding polymorphism allows us to defines methods in the child class that
have the same name as the methods in the parent class. This process of re-implementing the
inherited method in the child class is known as Method Overriding.

Advantage of method overriding

It is effective when we want to extend the functionality by altering the inherited method. Or the
method inherited from the parent class doesn’t fulfill the need of a child class, so we need to
re-implement the same method in the child class in a different way.
Method overriding is useful when a parent class has multiple child classes, and one of that
child class wants to redefine the method. The other child classes can use the parent class
method. Due to this, we don’t need to modification the parent class code
In polymorphism, Python first checks the object’s class type and executes the appropriate
method when we call the method. For example, If you create the Car object, then Python calls
the speed() method from a Car class.

Page 24
In this example, we have a vehicle class as a parent and a ‘Car’ and ‘Truck’ as its sub-class.
But each vehicle can have a different seating capacity, speed, etc., so we can have the same
instance method name in each class but with a different implementation. Using this code can
be extended and easily maintained over time.

Python Code (5.4)


class Vehicle:

def __init__(self, name, color, price):


self.name = name
self.color = color
self.price = price

def show(self):
print('Details:', self.name, self.color, self.price)

def max_speed(self):
print('Vehicle max speed is 150')

def change_gear(self):
print('Vehicle change 6 gear')

# inherit from vehicle class


class Car(Vehicle):
def max_speed(self):

Page 25
print('Car max speed is 240')

def change_gear(self):
print('Car change 7 gear')

# Car Object
car = Car('Car x1', 'Red', 20000)
car.show()
# calls methods from Car class
car.max_speed()
car.change_gear()

# Vehicle Object
vehicle = Vehicle('Truck x1', 'white', 75000)
vehicle.show()
# calls method from a Vehicle class
vehicle.max_speed()
vehicle.change_gear()

Result
Details: Car x1 Red 20000
Car max speed is 240
Car change 7 gear
Details: Truck x1 white 75000
Vehicle max speed is 150
Vehicle change 6 gear

Chapter 6. Modules

The module is a simple Python file that contains collections of classes and/or functions as well
as global variables and with having a .py extension file. It is an executable file and to organize
all the modules we have the concept called Package in Python.
The Python Standard Library is a collection of script modules accessible to a Python program
to simplify the programming process and removing the need to rewrite commonly used
commands.

The following are among the most important:


⮚ time
⮚ sys
⮚ os

Page 26
⮚ math
⮚ random

A user defined modules are module that the user creates. Next in this chapter you will learn
how to create Modules, Namespace and packages.

In Python, you use the import keyword to make code in one module available in another.
Imports in Python are important for structuring your code effectively. Using imports properly will
make you more productive, allowing you to reuse code while keeping your projects
maintainable.

Python Code (6.1)


import math
print(math.pi)
Result
3.141592653589793

You can import specific parts of a module and to rename the module as you import it. The
following code imports only the pi variable from the math module
Python Code (6.2)
from math import pi
print(pi)
Result
3.141592653589793

In 6.2, if you try to write:


Print(math.pi)
You will get the following error:
NameError: name 'math' is not defined

You can also rename modules and attributes as they’re imported


Python Code (6.3)
import math as m
print(m.pi)
Result
3.141592653589793

To create a user-defined module, follow the following example for factorial:


Open Notepad and type the following code:

Page 27
def fact(n):
return 1 if n == 1 else n * fact(n-1)

Press “save” then select the folder where your other python codes are.
In “Save as type” select “All Files”
In the File name type: fact.py
Press “Save”

Python Code (6.4)


from fact import fact
print(fact(5))
Result
125

Create a module computer:


class computer:
def __init__(self, core, ram, hd):
self.core = core
self.ram = ram
self.hd = hd

def printSpec(self):
print(self.core,",", self.ram,",",self.hd)

Create the code below

Python Code (6.5)


from computer import computer

newPC = computer("Core i7","256 MB","1 TB SDD")


newPC.printSpec()
Result

Page 28
Core i7 , 256 MB , 1 TB SDD

For reasons of efficiency, a module is only loaded once per interpreter session therefore, if you
make a change to a module you need to reload it, you need to either restart the interpreter.
Add exit() after the first line, run the code and the interpreter will restart. Remove the and
exit() continue.

Chapter 7. Data Abstraction, Information Hiding and Encapsulation


Abstract classes are classes that contain one or more abstract methods. An abstract method
is a method that is declared, but contains no implementation. Abstract classes cannot be
instantiated, and require subclasses to provide implementations for the abstract methods.

In abstraction, the users are familiar with the purpose of the class's methods, but they don't
know how they solve the purpose, which means that they know the inputs and expected
outputs, but the inner workings are hidden.

There are plenty of real-life examples, like when we press a button on a TV remote to change
the channel; we don’t know how it does that. We are just interested in the fact that when we
press a particular button, it changes the channel.

Smartphones are another example of abstraction; we are unaware of any of the internal
functionalities of the phone. We are just concerned with what we need to do to do a task.

For example, we don’t know how the phone records a video by pressing the record button, we
just touch/press a button, and it does that. There are numerous other examples of abstraction
for us to observe in the real world.

We can understand from the above examples that abstraction helps us make everything more
user-friendly and less complex. In the context of programming, Abstraction is used to make the
life of other developers easy. For example, in Python we do not know how the sort() function
of the list class works, we just use this function, and it sorts a list for us.

As we have discussed above, abstract classes are used to create a blueprint of our classes as
they don't contain the method implementation. This is a very useful capability, especially in
situations where child classes should provide their own separate implementation. Also, in
complex projects involving large teams and a huge codebase, it is fairly difficult to remember
all the class names.

Let’s see an example:


Create a text file shape.py that contains the following code:

Page 29
from abc import ABC, abstractmethod

class Shape(ABC):
def __init__(self, shape_name):
self.shape_name = shape_name

@abstractmethod
def draw(self):
pass

Python does provide a module that allows us to define abstract classes. The module we can
use to create an abstract class in Python is abc(abstract base class) module. We need to
inherit the ABC class from the abc module in Python and abstractmethod.
To define an abstract method we use the @abstractmethod decorator of the abc module. It
tells Python that the declared method is abstract and should be overridden in the child classes.

As you may say that we have declared an undefined method draw(). This method will be
implemented by the other classes that inherit this class.

The method draw() after the @abstractmethod decorator will be implemented by the other
classes that inherit this class.

Python Code (7.1)


from Shape import Shape

class Circle(Shape):
def __init__(self):
super().__init__("circle")

def draw(self):
print("Drawing a Circle")

class Square(Shape):
def __init__(self):
super().__init__("Square")

def draw(self):
print("Drawing a Square")

circle = Circle()
circle.draw()

Page 30
square = Square()
square.draw()
Result
Drawing a Circle
Drawing a Square

As we mentioned above, the abstract class is a blueprint of our classes, therefore you cannot
in this example create a child class without the method draw() otherwise you will get the
following error:

Python Code (7.1)


from Shape import Shape

class Circle(Shape):
def __init__(self):
super().__init__("circle")

def draw(self):
print("Drawing a Circle")

class Square(Shape):
def __init__(self):
super().__init__("Square")

# def draw(self):
# print("Drawing a Square")

circle = Circle()
circle.draw()
square = Square()
square.draw()
Result
Drawing a Drawing a Circle
---------------------------------------------------------------
------------
TypeError Traceback (most
recent call last)
~\AppData\Local\Temp\ipykernel_9636\1179070487.py in <module>
17 circle = Circle()
18 circle.draw()
---> 19 square = Square()
20 square.draw()
21 square.test()

Page 31
TypeError: Can't instantiate abstract class Square with
abstract method draw

Encapsulation is one of the fundamental concepts in object-oriented programming (OOP) in


Python. It describes the idea of wrapping data and the methods that work on data within one
unit. This puts restrictions on accessing variables and methods directly and can prevent the
accidental modification of data. A class is an example of encapsulation as it encapsulates all
the data that is member functions, variables, etc. Now, there can be some scenarios in which
we need to put restrictions on some methods of the class so that they can neither be accessed
outside the class nor by any subclasses.

Data hiding is also known as data encapsulation and it is the process of hiding the
implementation of specific parts of the application (attributes and/or methods) from the user.
Data hiding combines members of class thereby restricting direct access to the members of
the class. Data hiding plays a major role in making an application secure and more robust.

In Python, the process of encapsulation and data hiding works simultaneously. Data
encapsulation hides the private methods on the other hand data hiding hides only the data
components. The robustness of the data is also increased with data hiding. The private access
specifier is used to achieve data hiding. There are two types of access specifiers, private and
protected.

Private: Private variables/methods are those variables/methods that should neither be


accessed outside the class nor by any base class. To define a private method prefix the
member name with the double underscore “__”. Note: The __init__ method is a constructor
and runs as soon as an object of a class is instantiated.

in the below example, attribute age is public and any instance can change it without any
restrictions:

Python Code (7.1)


class Person:
def __init__(self):
self.age = None
self.name = None

def PrintData(self):
print(self.name," is a ",self.age," years old")

Alain = Person()
Alain.name = "Alain"
Alain.age = -5
Alain.PrintData()

Page 32
Result
Alain is a -5 years old

and the result is wrong.

Once the attribute age becomes private (by adding double underscore “__”), the instance will
not have access to change it.
Python Code (7.2)
class Person:
def __init__(self):
self.__age = 0
self.name = None

def PrintData(self):
print(self.name," is a ",self.__age," years old")

Alain = Person()
Alain.name = "Alain"
Alain.__age = -5
Alain.PrintData()
Result
Alain is a 0 years old

As you can see, the value of class attribute __age didn’t change and the Alain.__age =
-5 is an instance attribute not a class attribute.

In order to change the value of private attributes, write a class method that makes necessary
verifications before assigning value to private variables.

Python Code (7.3)


class Person:
def __init__(self):
self.__age = 0
self.name = None

def PrintData(self):
print(self.name," is a ",self.__age," years old")

def setAge(self,age):
if age > 15 and age < 80:
self.__age = age
return True
else:

Page 33
return False

def getAge(self):
return(self.__age)

Alain = Person()
Alain.name = "Alain"
if Alain.setAge(20) == False:
print("Invalid age. Must be between 15 and 80")
else:
Alain.PrintData()
print(Alain.getAge())

Result
Alain is a 20 years old
20

Protected: Protected variables are those data members of a class that can be accessed within
the class and the classes derived from that class. To define a protected variable/method, we
use the underscore ‘_’ symbol to determine the access control of a data member in a class.

Python Code (7.4)


class Person:
def __init__(self):
self._age = 0
self.name = None

class Student(Person):
def PrintData(self):
print(self.name," is a ",self._age," years old")

def setAge(self,age):
if age > 15 and age < 80:
self._age = age
return True
else:
return False

def getAge(self):
return(self._age)

Alain = Student()
Alain.name = "Alain"
if Alain.setAge(20) == False:
print("Invalid age. Must be between 15 and 80")

Page 34
else:
Alain.PrintData()
print(Alain.getAge())
Result
Alain is a 20 years old
20

I must note that an attribute with a single leading underscore _ does not mean anything special
to the python interpreter. But, it’s one of those strong conventions in the world of Pythonistas.
If you see one, it means that you should not access such attributes from outside the class. You
can even observe that even in some corners of the official Python documentation, attributes
with a single leading underscore _ are called “protected.”

To make them protected property decorator is used.

Property decorator
Let’s see Property decorator at work using example given below:

Python Code (7.5)


class Person():
def __init__(self, name):
self.name = name
self._theHiddenAge = None

def setAge(self,age):
if age > 5 and age < 50:
self._theHiddenAge = age

@property
def age(self):
return self._theHiddenAge

def __str__(self):
return f'Name: {self.name}, Age: {self._theHiddenAge}'

p1 = Person("Bob")
p1.setAge(25)
print(p1)
print(p1.name,p1.age)
#p1.age = 30 #will give error
Result
Name: Bob, Age: 25
Bob 25

Page 35
Advantages of Data Hiding:
1. It helps to prevent damage or misuse of volatile data by hiding it from the public.
2. The class objects are disconnected from the irrelevant data.
3. It isolates objects as the basic concept of OOP.
4. It increases the security against hackers that are unable to access important data.
Disadvantages of Data Hiding:
1. It enables programmers to write lengthy code to hide important data from common
clients.
2. The linkage between the visible and invisible data makes the objects work faster,
but data hiding prevents this linkage.

Chapter 8. Exception handling


There are mainly three kinds of distinguishable errors in Python: syntax errors, exceptions
and logical errors.

Syntax errors are similar to grammar or spelling errors in a Language. If there is such an error
in your code, Python cannot start to execute your code. You get a clear error message stating
what is wrong and what needs to be fixed. Therefore, it is the easiest error type you can fix.

Exceptions may occur in syntactically correct code blocks at run time. When Python cannot
execute the requested action, it terminates the code and raises an error message.
Trying to read from a file which does not exist, performing operations with incompatible types
of variables, dividing a number by zero are common exceptions that raise an error in Python.
We must eliminate the syntax errors to run our Python code, while exceptions can be handled
at runtime.

Logical errors are the most difficult errors to fix as they don’t crash your code and you don’t
get any error message. If you have logical errors, your code does not run as you expected.
Using incorrect variable names, code that is not reflecting the algorithm logic properly, making
mistakes on boolean operators will result in logical errors.

Try and Except Statement – Catching Exceptions


Try and except statements are used to catch and handle exceptions in Python. Statements that
can raise exceptions are kept inside the try clause and the statements that handle the exception
are written inside the except clause.

Python Code (8.1)


a = 10
b = 5
c = 0

try:
r = a/b
print(r)

Page 36
r = a/c
print(r)
except:
print("An error occurred")
Result
2.0
An error occurred

A try statement can have more than one except clause, to specify handlers for different
exceptions. for example if the function name is misspelled

Python Code (8.2)


def square(a):
return a*a;

a = 10
b = 5
c = 0

try:
r = squar(a)
print(r)
r = a/c
print(r)
except ZeroDivisionError:
print("ZeroDivisionError Occurred")
except NameError:
print("NameError Occurred")
Result
NameError Occurred

or a division by zero occurred

Python Code (8.3)


def square(a):
return a*a;

a = 10
b = 5
c = 0

try:
r = square(a)
print(r)

Page 37
r = a/c
print(r)
except ZeroDivisionError:
print("ZeroDivisionError Occurred")
except NameError:
print("NameError Occurred")
Result
100
ZeroDivisionError Occurred

In python, you can also use the else clause on the try-except block which must be present after
all the except clauses. The code enters the else block only if the try clause does not raise an
exception.

Python Code (8.4)


def square(a):
return a*a;

a = 10
b = 5
c = 0

try:
r = square(a)
print(r)
r = b/a
print(r)
except ZeroDivisionError:
print("ZeroDivisionError Occurred")
except NameError:
print("NameError Occurred and Handled")
else:
print("Done")
Result
100
0.5
Done

Chapter 9. Serialization and Deserialization JSON


JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for
humans to read and write. It is easy for machines to parse and generate. It is based on a subset
of the JavaScript Programming Language Standard.

Page 38
JSON is a text format that is completely language independent but uses conventions that are
familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript,
Perl, Python, and many others. These properties make JSON an ideal data-interchange
language.

JSON is built on two structures:


● A collection of name/value pairs. In various languages, this is realized as an object,
record, struct, dictionary, hash table, keyed list, or associative array.
● An ordered list of values. In most languages, this is realized as an array, vector, list, or
sequence.

These are universal data structures. Virtually all modern programming languages support them
in one form or another. It makes sense that a data format that is interchangeable with
programming languages also be based on these structures.

JSON example
{
"firstName": "Jane",
"lastName": "Doe",
"hobbies": ["running", "sky diving", "singing"],
"age": 35,
"children": [
{
"firstName": "Alice",
"age": 6
},
{
"firstName": "Bob",
"age": 8
}
]
}

Python comes with a built-in package called json for encoding and decoding JSON data.
You can use it by add at the top of your code:
import json

Converting from JSON to Python is usually called deserialization.


Converting from Python to JSON is usually called serialization.

Page 39
deserialization: turning JSON encoded data into Python objects.
The json.load() is used to read the JSON document from file and The json.loads()
is used to convert the JSON String document into the Python dictionary.
Simple dictionary:

Python Code (9.1)


import json

# some JSON:
JSON = '{ "name":"Bob", "age":30, "city":"Kuwait"}'

# parse x:
PyDict = json.loads(JSON)

# the result is a Python dictionary:


for v in PyDict:
print(v,PyDict[v])

Result
name Bob
age 30
city Kuwait

Dictionary with List

Python Code (9.2)


import json

person = '{"name": "Bob", "languages": ["English", "French"]}'

PyDict = json.loads(person)

print("Mr.",PyDict['name']," knows following languages: ",end='')


for l in PyDict['languages']:
print(l,end=' ')

Result
Mr. Bob knows following languages: English French

List with Dictionary

Python Code (9.3)

Page 40
import json

students = '[ {"name":"Bob", "age":30, "city":"Kuwait"}, \


{"name":"Johnny", "age":25, "city":"Beirut"} \
]'
Pylist = json.loads(students)

for l in Pylist:
for d in l:
print(d,l[d],end = ' ')
print('')
Result
name Bob age 30 city Kuwait
name Johnny age 25 city Beirut

More complex JSON

Python Code (9.4)


import json

person = '[ { \
"firstName": "Jane", \
"lastName": "Doe", \
"hobbies": ["running", "skydiving", "singing"], \
"age": 35, \
"children": [ \
{ \
"firstName": "Alice", \
"age": 6 \
}, \
{ \
"firstName": "Bob", \
"age": 8 \
} \
] \
} , { \
"firstName": "Bob", \
"lastName": "Korsky", \
"hobbies": ["Reading", "Football"], \
"age": 31, \
"children": [ \
{ \
"firstName": "Tom", \
"age": 2 \
}, \
{ \
"firstName": "Kate", \

Page 41
"age": 8 \
}, \
{ \
"firstName": "Alice", \
"age": 9 \
} \
] \
} ] '

Per = json.loads(person)
for p in Per:
print("Mr.",p["firstName"],p["lastName"]," is ",p["age"],"
likes: ",end=' ')
for h in p["hobbies"]:
print(h,end=' ')
print(" his children:",end=' ')
for c in p["children"]:
print(c["firstName"],c["age"],end=' ')
print('')
Result
Mr. Jane Doe is 35 likes: running skydiving singing his children: Alice 6 Bob 8
Mr. Bob Korsky is 31 likes: Reading Football his children: Tom 2 Kate 8 Alice 9

If your data is stored on a file named “per_file.json” that you would like to manipulate in memory.
You will still use the context manager, but this time you’ll open up the existing in read mode.

Python Code (9.5)


import json

with open("per_file.json", "r") as read_file:


Per = json.load(read_file)

for p in Per:
print("Mr.",p["firstName"],p["lastName"]," is ",p["age"],"
likes: ",end=' ')
for h in p["hobbies"]:
print(h,end=' ')
print(" his children:",end=' ')
for c in p["children"]:
print(c["firstName"],c["age"],end=' ')
print('')

Result
Mr. Jane Doe is 35 likes: running skydiving singing his children: Alice 6 Bob 8
Mr. Bob Korsky is 31 likes: Reading Football his children: Tom 2 Kate 8 Alice 9

Page 42
An application programming interface (API) is code that enables two software programs to
communicate. An API defines how a developer should request services from an operating
system (OS) or other application, and expose data within different contexts and across multiple
channels.

Any data can be shared with an application programming interface. APIs are implemented by
function calls composed of verbs and nouns; the required syntax is described in the
documentation of the application being called. For example, on a real estate website, one API
might publish available real estate properties by geography, while a second API provides
current interest rates and a third offers a mortgage calculator. We will discuss an example of
covid data to retrieve live information about numbers in each country.

An API response consists of the response body, headers, and the HTTP status code. The
response is can use JSON, XML and other format.

To read a JSON response there is a widely used library called urllib in python. This library
helps to open the URL and read the JSON response from the web. To use this library in python
and fetch JSON response we have to import the json and urllib in our code, The
json.loads() method returns JSON object. Below is the process by which we can read the
JSON response from a link or URL in python.
Approach:
● Import required modules.
● Assign URL.
● Get the response of the URL using urlopen().
● Convert it to a JSON response using json.loads().
● Display the generated JSON response.

Python Code (9.6)


from urllib.request import urlopen
import json

url = "https://pomber.github.io/covid19/timeseries.json"
response = urlopen(url)
covid_data = json.loads(response.read())
prevDay = 0
for days in covid_data["Kuwait"]:
if days['deaths']-prevDay > 10:

print(days['date'],days['confirmed'],days['deaths'],days['deaths']
-prevDay)
prevDay = days['deaths']

Page 43
Python Code (9.7)
from urllib.request import urlopen
import json

url= "https://api.nasa.gov/neo/rest/v1/neo/browse?api_key=DEMO_KEY"

response = urlopen(url)
data = json.loads(response.read())
for obj in data['near_earth_objects']:
for nextapp in obj["close_approach_data"]:
if nextapp["close_approach_date_full"][0:4] == '2024':
print(obj["name"],nextapp["close_approach_date_full"],nextapp["mis
s_distance"]["kilometers"])
from

serialization: turning data from Python objects into JSON encoded data.
The json.dump() is used to convert Python objects into JSON string.

with open("data_file.json", "w") as write_file:


json.dump(data, write_file)

Python Code (9.8)


class Players:
def __init__(self, fullName, age, HT, WT):
self.fullName = fullName
self.age = age
self.HT = HT
self.WT = WT

def getList(self):
pl = []
pl.append(self.fullName)
pl.append(self.age)
pl.append(self.HT)
pl.append(self.WT)
return pl

PlayerDict = {}
temp = Players("Messi, Lionel",35,1.7,71.85)
PlayerDict[10] = temp.getList()
temp = Players("Di Maria, Angel",34,1.8,74.84)
PlayerDict[11] = temp.getList()

Page 44
temp = Players("Gomez, Papu",34,1.67,67.86)
PlayerDict[17] = temp.getList()
temp = Players("tamendi, Nicolas",34,1.83,82.83)
PlayerDict[19] = temp.getList()

with open("arg_file.json", "w") as write_file:


json.dump(PlayerDict, write_file)

Page 45

You might also like