CMPS 201 Course
CMPS 201 Course
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".
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.
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:
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:
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.
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:
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)
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:
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
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
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.
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.
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
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.
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.
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.
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
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
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.
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.
class Parent_2:
def f2(self):
print("Function of parent_2 class.")
class Parent_3:
def f3(self):
print("function of parent_3 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)
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)
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.
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
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.
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.
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
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.
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.
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:
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.
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.
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.
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')
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.
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.
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
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”
def printSpec(self):
print(self.core,",", self.ram,",",self.hd)
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.
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.
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.
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:
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
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.
in the below example, attribute age is public and any instance can change it without any
restrictions:
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
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.
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.
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.”
Property decorator
Let’s see Property decorator at work using example given below:
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.
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:
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
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
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.
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
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.
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
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:
# some JSON:
JSON = '{ "name":"Bob", "age":30, "city":"Kuwait"}'
# parse x:
PyDict = json.loads(JSON)
Result
name Bob
age 30
city Kuwait
PyDict = json.loads(person)
Result
Mr. Bob knows following languages: English French
Page 40
import json
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
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.
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.
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.
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()
Page 45