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

UNIT 2_Python

ok

Uploaded by

nehalchouhan890
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views

UNIT 2_Python

ok

Uploaded by

nehalchouhan890
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 55

UNIT II

(Python Data Structure, functions & classes/objects)

Lists, Tuples, Sets, Dictionaries, Sorting Dictionaries, Functions


in python, Object-Oriented Programming: Classes and Objects,
Creating Classes in Python, Creating Objects in Python

Lists in Python
A built - in data type that stores set of values.
It can store elements of different types (integer, float, string, etc.)
marks = [87, 64, 33, 95, 76] # marks[0], marks[1] …
student = [“karan”, 85, “Delhi”] #student[0], student[1]…
student[0] = “Arjun” #allowed in python
len(student) #returns length

marks = [94.4, 87.5, 95.2, 66.4, 45.1]


print(marks)
print(type(marks))
print(marks[0])
print(marks[1])
print(len(marks))

We can store elements of different type in a single list. For example:

student = ["karan", 95.4, 17, "delhi"]


print(student)

Note: Strings are immutable and lists are mutable in python. Mutable
means to change and immutable means not changed.
Example:
str = "hello"
print(str[0])
str[0] = "y"

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


In above example we can access each character of string but we cannot
assign value in string. The line str[0] = “y” will give error. But in list we may
assign value as shown below:
student = ["karan", 95.4, 17, "delhi"]
print(student[0])
student[0] ="arjun"
print(student)

List Slicing
Similar to String Slicing.
List_name[starting_idx: ending_idx] #ending index is not included
marks = [87, 64, 33, 95, 76]
marks[1:4] is [64,33,95]
marks[ :4] is same as marks[0:4]
marks[1: ] is same as marks[1:len(marks)]
marks[-3:-1] is [33,95]

marks = [87, 64, 33, 95, 76]


print(marks[-3:-1]) # in index -1 value is 76 and -2 is 95 and so on…

List Methods
list = [2, 1, 3]
list.append(4) #adds one element at the end [2, 1, 3, 4]
list.sort() #sorts in ascending order [1, 2, 3]
list.sort(reverse = True) #sorts in descending order [3, 2, 1]
list.reverse() #reverses list [3, 1, 2]
list.insert(idx, el) #insert element at index
If list = [2, 1, 3, 1]
list.remove(1) #removes first occurrence of element [2, 3, 1]
list.pop(idx) #removes element at idx

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


list = [87, 64, 33, 95, 76] list = ["banana","litchi","apple"]
list.append(100.3) print(list.sort(reverse=True))
print(list) print(list)

list = [87, 64, 33, 95, 76] list = ["banana","litchi","apple"]


list.append(100.3) print(list.sort())
print(list.sort()) print(list)
print(list)

list = [87, 64, 33, 95, 76] list = ["banana","litchi","apple"]


list.append(100.3) list.reverse()
print(list) print(list)
print(list.sort(reverse=True))
print(list)

list = [2, 1, 3] list = [2, 1, 3, 1]


list.insert(1, 5) list.pop(2)
print(list) print(list)

Note: we have lots of methods with list in visual studio. For example, if
we type list. In visual studio then it will show so many methods as
shown below:

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


# Initial list
my_list = [10, 20, 30]

# 1. append() - Adding an element at the end of the list


my_list.append(40)
print("After append:", my_list) # [10, 20, 30, 40]

# 2. extend() - Extending the list by another list


my_list.extend([50, 60])
print("After extend:", my_list) # [10, 20, 30, 40, 50, 60]

# 3. insert() - Inserting an element at a specific index


my_list.insert(1, 15)
print("After insert:", my_list) # [10, 15, 20, 30, 40, 50, 60]

# 4. remove() - Removing the first occurrence of a value


my_list.remove(30)
print("After remove:", my_list) # [10, 15, 20, 40, 50, 60]

# 5. pop() - Removing and returning the last element (or a


specific index)
popped_element = my_list.pop()
print("After pop:", my_list, "Popped element:",
popped_element) # [10, 15, 20, 40, 50] Popped element: 60

# 6. clear() - Clearing all elements from the list


my_list.clear()
print("After clear:", my_list) # []

# Re-populating the list


my_list = [10, 20, 30, 40, 50]

# 7. index() - Finding the index of an element


index_40 = my_list.index(40)
print("Index of 40:", index_40) # 3

# 8. count() - Counting occurrences of an element


count_20 = my_list.count(20)
print("Count of 20:", count_20) # 1

# 9. sort() - Sorting the list in ascending order


my_list.sort(reverse=True)

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


print("After sort (descending):", my_list) # [50, 40, 30, 20, 10]

# 10. reverse() - Reversing the list


my_list.reverse()
print("After reverse:", my_list) # [10, 20, 30, 40, 50]

# 11. copy() - Creating a shallow copy of the list


copied_list = my_list.copy()
print("Copied list:", copied_list) # [10, 20, 30, 40, 50]

Output:
After append: [10, 20, 30, 40]
After extend: [10, 20, 30, 40, 50, 60]
After insert: [10, 15, 20, 30, 40, 50, 60]
After remove: [10, 15, 20, 40, 50, 60]
After pop: [10, 15, 20, 40, 50] Popped element: 60
After clear: []
Index of 40: 3
Count of 20: 1
After sort (descending): [50, 40, 30, 20, 10]
After reverse: [10, 20, 30, 40, 50]
Copied list: [10, 20, 30, 40, 50]

Tuples in Python
A built-in data type that lets us create immutable sequences of values.
tup = (87, 64, 33, 95, 76) #tup[0], tup[1]…
tup[0] = 43 #not allowed in python
Note: We can access value but cannot assign value in tuple.
tup1 = ( )
tup2 = ( 1, )
tup3 = (1, 2, 3)

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


tup1 =("banana","litchi","apple")
tup2 =(2, 1, 3, 1)
print(tup1)
print(tup2)
print(tup1[0])
print(tup1[1])
print(tup1[2])
print(tup2[0])
print(tup2[1])
print(tup2[2])

tup = ( )
print(tup)
print(type(tup))

Here class type is tuple When we remove comma after 1


then class type is integer
tup =(1,)
print(tup) tup =(1)
print(type(tup)) print(tup)
print(type(tup))

When we remove comma after tup =(1, 2, 3, 4)


hello then here class type is print(tup[1:3])
string. So we have to write
comma for class type “tuple”.

tup =("hello")
print(tup)
print(type(tup))

Tuple Methods
tup(2, 1, 3, 1)
tup.index(el) #returns index of first occurrence tup.index(1) is 1
tup.count(el) #counts total occurrences tup.count(1) is 2
tup =(1, 2, 3, 4) tup =(1, 2, 3, 4, 2, 2)
print(tup.index(2)) print(tup.count(2))

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Practice Questions:
WAP to ask user to enter WAP to check if a list
names of their 3 favorite contains a palindrome of
movies and store them in a elements. (Hint: use copy()
list. method)
[1, 2, 3, 2, 1] or [1, “abc”,
movies = [] #blank list “abc”, 1]
movie1 = input("enter name of 1st
movie:") list1 = [1, 2, 1]
movie2 = input("enter name of 2nd
movie:") copy_list1 = list1.copy()
movie3 = input("enter name of 3rd copy_list1.reverse()
movie:")
if(copy_list1 == list1):
movies.append(movie1) print("pallindrom")
movies.append(movie2) else:
movies.append(movie3) print("not a pallindrom")
print(movies)
Another program
Another way of writing
list1 = [1, "abc", "abc", 1]
program
copy_list1 = list1.copy()
movies = [] #blank list copy_list1.reverse()
movies.append(input("enter name of
1st movie:")) if(copy_list1 == list1):
movies.append(input("enter name of print("pallindrom")
2nd movie:")) else:
movies.append(input("enter name of print("not a pallindrom")
3rd movie:"))

print(movies)

WAP to count the number of


students with the “A” grade
in the following tuple:
[“C”, “D”, “A”, “A”, “B”, “B”,
“A”]
Store the above values in a
list and sort them from “A” to
“D”.

grade = ("C", "D", "A", "A", "B", "B", "A"


)
print(grade.count("A"))

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Another Part

grade = ["C", "D", "A", "A", "B", "B", "A"


]
grade.sort()
print(grade)

Dictionary in Python
Dictionaries are used to store data values in key: value pairs.
They are unordered, mutable(changeable) & don’t allow duplicate keys.
(string, list and tuple have index so they are ordered.)

dict[”name”], dict[”cgpa”], dict[”marks”] # to access values.


dict[”key”] = “value” #to assign or add new

We may store string value, List and tuples can be stored in a


integer, float and Boolean value dictionary as shown below:
in a dictionary as shown below:
info = {
info = { "name" : "BIT DURG",
"key" : "value", "subjects" : ["python", "C", "java"],
"name" : "BIT DURG", "topics" :("dict", "set"),
"learning" : "coding", "learning" : "coding",
"age" : 35, "age" : 35,
"is_adult" : True, "is_adult" : True,
"marks" : 94.4 "marks" : 94.4
} }
print(info) print(info)

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


To access values from dictionary To change the value and new key
and value is added.
info = {
"name" : " BIT DURG ", info = {
"subjects" : ["python", "C", "java"], "name" : " BIT DURG ",
"topics" :("dict", "set"), "subjects" : ["python", "C", "java"],
"learning" : "coding", "topics" :("dict", "set"),
"age" : 35, "learning" : "coding",
"is_adult" : True, "age" : 35,
"marks" : 94.4 "is_adult" : True,
} "marks" : 94.4
print(info["name"]) }
print(info["topics"]) info["name"] = "kauleshwar"
print(info["subjects"]) #changed the value (mutable)
print(info["age"]) info["surname"] = "Prasad" #added
new key and value
print(info)

Output:

{'name': 'kauleshwar', 'subjects':


['python', 'C', 'java'], 'topics':
('dict', 'set'), 'learning': 'coding',
'age': 35, 'is_adult': True, 'marks':
94.4, 'surname': 'Prasad'}

Null dictionary can be created

null_dict = { }
print(null_dict)

Note: key may be int, float, string or Boolean value.

Nested Dictionaries
Dictionary under dictionary
student = {
"name" : "Kauleshwar Prasad",
"subjects" : {
"phy" : 97,
"chem" : 98,
"math" : 95
#nested dictionary(subject dictionary under student dictionary)
}
}
print(student) # to display values of student dictionary
print(student["subjects"]) # to display values of subjects dictionary
print(student["subjects"]["chem"]) #to display marks of chem

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Output:

{'name': 'Kauleshwar Prasad', 'subjects': {'phy': 97, 'chem': 98, 'math':


95}}
{'phy': 97, 'chem': 98, 'math': 95}
98

Dictionary Methods
myDict.keys( ) #returns all keys
myDict.values( ) #returns all values
myDict.items( ) #returns all (key, val) pairs as tuples
myDict.get( “key““ ) # returns the value associated with the specified key in
the dictionary. If the key is not found, .get() returns None
myDict.update( newDict ) #inserts the specified items to the dictionary
student = {
"name" : "Kauleshwar Prasad",
"subjects" : {
"phy" : 97,
"chem" : 98,
"math" : 95
#nested dictionary(subject dictionary under student dictionary)
}
}
print(student.keys()) #returns all keys.

student = {
"name" : "Kauleshwar Prasad",
"subjects" : {
"phy" : 97,
"chem" : 98,
"math" : 95
#nested dictionary(subject dictionary under student dictionary)
}
}
print(student.values()) #returns all values

student = {
"name" : "Kauleshwar Prasad",
"subjects" : {
"phy" : 97,
"chem" : 98,
"math" : 95

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


#nested dictionary(subject dictionary under student dictionary)
}
}
print(student.items()) #returns key,value pairs

student = {
"name" : "Kauleshwar Prasad",
"subjects" : {
"phy" : 97,
"chem" : 98,
"math" : 95
#nested dictionary(subject dictionary under student dictionary)
}
}
print(student.get("name")) #return key according to the value

student = {
"name" : "Kauleshwar Prasad",
"subjects" : {
"phy" : 97,
"chem" : 98,
"math" : 95

}
}
student.update({"city": "delhi"}) #new key value pair updated
print(student)

Note: we can also write as below instead of above 2 sentences

new_dict = {“city” : “delhi”}


student.update(new_dict)
print(student)

Set in Python
Set is the collection of the unordered items.
Each element in the set must be unique & immutable.
List and dictionary cannot be stored in set because both are mutable.
Int, float, string, tuple and Boolean can be stored in set.
nums = { 1, 2, 3, 4 }
set2 = { 1, 2, 2, 2 } #repeated elements stored only once, so it resolved to {1,
2}
null_set = set( ) #empty set syntax

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


collection = {1,2,3,4}
print(collection)
print(type(collection))

collection = {1,2,3,4,5.7, "hello"} #float and string can also be stored.


print(collection)
print(type(collection))

collection = {1,2,3,4,2,3, "hello","world","hello"} #ignore duplicate


values.also unordered
print(collection)
print(type(collection))
print(len(collection)) #ignore duplicate values

collection = set( ) #syntax of empty set


print(type(collection))

Set Methods
set.add( el ) #adds an element
set.remove( el ) #removes the element
set.clear( ) #empties the set
set.pop( ) #removes a random value
set.union( set2 ) #combines both set values & returns new
set.intersection( set2 ) #combines common values & returns new

collection = {1,2,3,4,2,3, "hello","world","hello"}


print(collection)
collection.add(20) #adds an element 20
print(collection)
collection.add((1,2,3))#can add tuple but not a list
print(collection)
collection.remove("hello") #removes an element hello
print(collection)
print(collection.pop()) #pop random values
print(collection)
set1 = {1, 2, 3, 4}
set2 = {2, 5, 6}
print(set1.union(set2))
print(set1.intersection(set2))

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Practice question
Store following word meanings in a python dictionary : table : “a piece
of furniture”, “list of facts & figures” cat : “a small animal”

dictionary = {
"cat" :"a small animal",
"table": ["a piece of furniture", "list of facts and figures"]
}
print(dictionary)

You are given a list of subjects for students. Assume one classroom is
required for 1 subject. How many classrooms are needed by all
students. ”python”, “java”, “C++”, “python”, “javascript”, “java”,
“python”, “java”, “C++”, “C

subjects = {
"python", "java", "c++", "python","javascript”, “java”, “python”, “java”,
“C++”, “C"
}
print(len(subjects))

WAP to enter marks of 3 subjects from the user and store them in a
dictionary. Start with an empty dictionary & add one by one. Use
subject name as key & marks as value.

marks = { }

x = int(input("enter marks of phy:"))


marks.update({"phy" : x})

x = int(input("enter marks of chem:"))


marks.update({"chem" : x})

x = int(input("enter marks of math:"))


marks.update({"math" : x})

print(marks)

Figure out a way to store 9 & 9.0 as separate values in the set. (You
can take help of built-in data types)

values = {9, "9.0"} #for floating point representation we need to write it in a


string form
print(values)

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Another method:

values = {
("float", 9.0),
("int", 9)
}
print(values)

Functions in Python
• If a group of statements is repeatedly required then it is not recommended
to write these statements every time separately. We must define these
statements as a single unit and we can call that unit any number of times
based on our requirement without rewriting. This unit is nothing but
function.
• The main advantage of functions is code Reusability.
• In other languages, functions are known as methods, procedures,
subroutines etc.

a = 20
b = 10
print('The Sum : ', a + b)
print('The Difference : ', a - b)
print('The Product : ', a * b)

Output:
The Sum : 30
The Difference : 10
The Product : 200

a = 20
b = 10
print('The Sum : ', a + b)
print('The Difference : ', a - b)
print('The Product : ', a * b)
a = 200
b = 100
print('The Sum : ', a + b)
print('The Difference : ', a - b)
print('The Product : ', a * b)

Output:
The Sum : 30
The Difference : 10
The Product : 200

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


The Sum : 300
The Difference : 100
The Product : 20000
a = 20
b = 10
print('The Sum : ', a + b)
print('The Difference : ', a - b)
print('The Product : ', a * b)
a = 200
b = 100
print('The Sum : ', a + b)
print('The Difference : ', a - b)
print('The Product : ', a * b)
a = 2000
b = 1000
print('The Sum : ', a + b)
print('The Difference : ', a - b)
print('The Product : ', a * b)

Output:
The Sum : 30
The Difference : 10
The Product : 200
The Sum : 300
The Difference : 100
The Product : 20000
The Sum : 3000
The Difference : 1000
The Product : 2000000

Here the same code (3 lines) is repeating thrice in the code. Generally we never
recommended to wrtie the group of statements repeatedly in the program.
Problems of writing the same code repeatedly in the program:
1. Length of the program increases.
2. Readability of the program decreases.
3. No Code Reusabilty.
Solution of the above problem:
def calculate(a,b):
print('The Sum : ', a + b)
print('The Difference : ', a - b)
print('The Product : ', a * b)
a = 20
b = 10
calculate(a,b)

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Output:
The Sum : 30
The Difference : 10
The Product : 200
def calculate(a,b):
print('The Sum : ', a + b)
print('The Difference : ', a - b)
print('The Product : ', a * b)
a = 20
b = 10
calculate(a,b)
a = 200
b = 100
calculate(a,b)

Output:
The Sum : 30
The Difference : 10
The Product : 200
The Sum : 300
The Difference : 100
The Product : 20000

def calculate(a,b):
print('The Sum : ', a + b)
print('The Difference : ', a - b)
print('The Product : ', a * b)
a = 20
b = 10
calculate(a,b)
a = 200
b = 100
calculate(a,b)
a = 2000
b = 1000
calculate(a,b)

Output:
The Sum : 30
The Difference : 10
The Product : 200
The Sum : 300
The Difference : 100
The Product : 20000
The Sum : 3000
The Difference : 1000
The Product : 2000000

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


def calculate(a,b):
print('The Sum : ', a + b)
print('The Difference : ', a - b) # Function 'calculate()' executes 3 times
print('The Product : ', a * b)
calculate(20,10) # Function call
calculate(200,100)
calculate(2000,1000) #Concise code resulted because of code reusability

Output:
The Sum : 30
The Difference : 10
The Product : 200
The Sum : 300
The Difference : 100
The Product : 20000
The Sum : 3000
The Difference : 1000
The Product : 2000000

Note: We are writing the function once and calling that function 'n'
times.

Practice Questions
1. WAP which defines a function called add_numbers to add two
numbers and returns the result. Both numbers will be given as input
from user.

# Function to add two numbers


def add_numbers(a, b):
return a + b

# Main program
num1 = float(input("Enter the first number: "))
num2 = float(input("Enter the second number: "))

# Call the function and store the result


result = add_numbers(num1, num2)

# Print the result


print(f"The sum of {num1} and {num2} is: {result}")

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Types of Functions
Python supports 2 types of functions:
1. Built in Functions
2. User Defined Functions
1. Built in Functions
The functions which are coming along with Python software automatically,
are called built in functions or pre defined functions.
Eg:
id()
type()
input()
eval() etc..
2. User Defined Functions
The functions which are developed by programmer explicitly according to
business requirements, are called user defined functions.
Syntax to create user defined functions:
def function_name(parameters) : #mandatory
Stmt 1
Stmt 2
---
Stmt n
return value #optional
Eg 1: Write a function to print Hello message

def wish():
print("Hello Good Morning")
wish()
wish()
wish()

Output:
Hello Good Morning
Hello Good Morning
Hello Good Morning

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Parameters
Parameters are inputs to the function.
If a function contains parameters, then at the time of calling, compulsory we
should provide values, otherwise we will get error.
Types of Parameters in Python:
1. Positional Parameters:
In the case of positional arguments, number of arguments must be same.
In the case of positional arguments, order of the arguments is important.
def calc(a,b): # Here, 'a' & 'b' are called positional arguments
sum = a + b
sub = a - b
mul = a * b
div = a / b
return sum,sub,mul,div
calculation = calc(100,50)
print(calculation) # Positional arguments

Output:
(150, 50, 5000, 2.0)
def calc(a,b): # Positional Arguments
sum = a + b
sub = a - b
mul = a * b
div = a / b
return sum,sub,mul,div # In Python, when you return multiple values separated
by commas, they are returned as a tuple.
t = calc(100,50)
for x in t:
print(x)

Note: A for loop is used to iterate over the tuple t. During each iteration, the
variable x takes the value of the current element in the tuple.

Output:
150
50
5000
2.0
If we want to write above program without return statement then
program is as follows:

def calc(a, b): # Positional Arguments


sum_value = a + b
sub_value = a - b

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


mul_value = a * b
div_value = a / b

# Directly printing the results inside the function


print("Sum:", sum_value)
print("Subtraction:", sub_value)
print("Multiplication:", mul_value)
print("Division:", div_value)

# Function call
calc(100, 50)

Use of return

def calc(a, b):


sum_value = a + b
return sum_value

# Call the function and store the result


result = calc(100, 50)

# Use the returned value later


print("The result is:", result)

In this example, the calc function returns the sum of a and b, which
can be stored in a variable and printed later. This separation allows you
to use result for additional computations or decisions later in your
program.
While printing values directly can be simpler for small, one-off scripts
or quick tests, using return is a better practice for creating reusable,
maintainable, and flexible code, especially in larger programs or when
building libraries.

2. Keyword (i.e., Parameter name) Parameters:


In the case of keyword arguments, order of the arguments is not important.
In the case of keyword arguments, number of arguments must be same.

def calc(a,b): # keyword Arguments


sum = a + b
sub = a - b
mul = a * b
div = a / b
return sum,sub,mul,div
t = calc(a = 100,b = 50) # keyword Arguments
for x in t:
print(x)

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Output:
150
50
5000
2.0
def calc(a,b): # keyword Arguments
sum = a + b
sub = a - b
mul = a * b
div = a / b
return sum,sub,mul,div
t = calc(b = 50, a = 100) # keyword Arguments(order is different)
for x in t:
print(x)

Output:
150
50
5000
2.0
def calc(a,b): # keyword Arguments
sum = a + b
sub = a - b
mul = a * b
div = a / b
return sum,sub,mul,div
t = calc(100,b = 50) #perfectly valid
for x in t:
print(x)

Output:
150
50
5000
2.0
def calc(a,b): # keyword Arguments
sum = a + b
sub = a - b
mul = a * b
div = a / b
return sum,sub,mul,div
t = calc(b = 50, 100) # It is invalid, because positional argument
should follow keyword argument
for x in t:
print(x)

def calc(a,b): # keyword Arguments


sum = a + b

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


sub = a - b
mul = a * b
div = a / b
return sum,sub,mul,div
t = calc(50, a = 50) # It is also invalid
for x in t:
print(x)

Some Practice questions

# Function to check if a number is even or odd

def check_even_odd(num):
if num % 2 == 0:
return "Even"
else:
return "Odd"

# Main program
number = int(input("Enter a number: "))
result = check_even_odd(number)
print(f"The number {number} is {result}.")

# Function to find the factorial of a number

def factorial(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial(n - 1)

# Main program
num = int(input("Enter a number: "))
fact = factorial(num)
print(f"The factorial of {num} is {fact}.")

# Function to find the maximum of three numbers

def find_max(a, b, c):


return max(a, b, c)

# Main program

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


num1 = float(input("Enter first number: "))
num2 = float(input("Enter second number: "))
num3 = float(input("Enter third number: "))

maximum = find_max(num1, num2, num3)


print(f"The maximum of {num1}, {num2}, and {num3} is {maximum}.")

# Function to reverse a string

def reverse_string(s):
return s[::-1]

# Main program
string = input("Enter a string: ")
reversed_str = reverse_string(string)
print(f"The reversed string is: {reversed_str}")

# Function to check if a string is a palindrome

def is_palindrome(s):
return s == s[::-1]

# Main program
string = input("Enter a string: ")
if is_palindrome(string):
print(f"'{string}' is a palindrome.")
else:
print(f"'{string}' is not a palindrome.")

3. Default Parameters:

• A default parameter in Python is a parameter in a function that has a


default value if no argument is passed during the function call.
• When defining a function, you can assign a default value to a parameter,
which makes it optional for the caller to provide that argument.
• If the argument is not provided, the default value is used; otherwise, the
given argument is used.

def greet(name="BITIANS"):
print(f"Hello, {name}!")

greet("BIT DURG") # Output: Hello, BIT DURG!


greet() # Output: Hello, BITIANS!

• In the example, name=" BITIANS " means that name has a default value
of " BITIANS ".

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


• If the caller provides an argument (e.g., " BIT DURG "), the function
will use that value.
• If no argument is provided, the function will use the default value "
BITIANS ".

def wish(msg,name ='Guest'): Description: function wish takes


print(msg,name) two parameters: msg and name. The
wish('Hello','BITIANS') name parameter has a default value
of 'Guest'. Inside the function, it
Output: Hello BITIANS prints the message msg followed by
the name.
When the function wish('Hello',
'BITIANS') is called, the following
happens:
• msg is set to 'Hello'
• name is set to 'BITIANS'

def wish(msg,name ='Guest'): Description:


print(msg,name) The function wish is defined with two
wish('Hello') parameters: msg and name, where
name has a default value of 'Guest'
Output: Hello Guest
Inside the function, the print
statement prints the values of msg
and name.

The function is called with the


argument 'Hello' for msg, and since
no argument is provided for name, it
defaults to 'Guest'.
def wish(msg,name ='Guest'): Description:
print(msg,name) The function wish takes two
wish() parameters: msg and name. The msg
parameter is required, while the
Output: name parameter has a default value
TypeError: wish() missing 1 of 'Guest'.
required positional argument: When the function wish is called
'msg' without arguments Python raises a
TypeError because the required
positional argument msg is missing.
The correct way to call this function
without specifying the name
argument is to at least provide a
value for the msg parameter, like
below:
wish(‘hello’)

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


def wish(name ='Guest',msg='Good
Morning'):
print('Hello',name,msg)
wish()

Output:
Hello Guest Good Morning
def wish(marks,age,name = 'Guest', msg = 'Good Morning'):
print('Student Name:',name)
print('Student Age:',age)
print('Student Marks:',marks)
print('Message:',msg)
wish(99,48,'Karthi') # Valid

Output:
Student Name: Karthi
Student Age: 48
Student Marks: 99
Message: Good Morning

def wish(marks,age,name = 'Guest', msg = 'Good Morning'):


print('Student Name:',name)
print('Student Age:',age)
print('Student Marks:',marks)
print('Message:',msg)
wish(age=48,marks = 100) # valid

Output:
Student Name: Guest
Student Age: 48
Student Marks: 100
Message: Good Morning

def wish(marks,age,name = 'Guest', msg = 'Good Morning'):


print('Student Name:',name)
print('Student Age:',age)
print('Student Marks:',marks)
print('Message:',msg)
wish(100,age=46,msg='Bad Morning',name='Karthi') # valid

Output:
Student Name: Karthi
Student Age: 46
Student Marks: 100
Message: Bad Morning

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


4. Variable length Parameters:

• Variable length parameters in Python allow a function to accept an


arbitrary number of arguments.
• These are useful when we don't know beforehand how many arguments
might be passed to the function.
• We can declare a variable length argument with * symbol as follows
def f1(*n):

• We can call this function by passing any number of arguments including


zero number. Internally all these values represented in the form of tuple.

def sum(a,b):
print(a+b)
sum(10,20)

Output:
30

After some time my requirement is as follows:


sum(10,20,30)

def sum(a,b):
print(a+b)
sum(10,20,30)
It will give error as follows:
TypeError: sum() takes 2 positional arguments but 3 were given

def sum(a,b,c):
print(a+b+c)
sum(10,20,30) # we have to go for another sum() function

Output:
60
Now it is working correctly.

After some time my requirement is as follows: sum(10,20,30,40)

def sum(a,b,c):
print(a+b+c)
sum(10,20,30,40)

Output:
TypeError: sum() takes 3 positional arguments but 4 were given

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Once again the same problem. we should go for another sum() function.

def sum(a,b,c,d):
print(a+b+c+d)
sum(10,20,30,40) # we have to go for another sum() function

Output:
100

If you change the number of arguments, then automatically for every


change, compulsorily we need to go for new function unnecessarily. Because
of this, length of the code is going to increase. To overcome this problem,
we should go for variable length arguments.

def sum(*n):
result =0
for x in n:
result = result + x
print(result)
sum(10,20,30,40) # Here, 'n' is a variable length argument.

Description:

1. Function Definition (def sum(*n):)


• The sum function is defined with a special parameter *n.
• The *n allows the function to accept any number of arguments, which
will be packed into a tuple called n.

2. Initializing result (result = 0)

3. for x in n:
• The function loops over each element in the tuple n (which contains
all the arguments passed to the function).

4. Accumulating the Sum (result = result + x)


For each value x in n, the current value of x is added to result. This
updates result to reflect the running total.

5. Printing the Result (print(result))


After the loop has finished, the function prints the final value of
result, which is the sum of all the arguments.

6. Calling the Function (sum(10, 20, 30, 40))


• The function is called with four arguments: 10, 20, 30, and 40.
• These values are packed into a tuple and passed to the function,
resulting in the sum 10 + 20 + 30 + 40 = 100.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


def sum(*n):
result =0
for x in n:
result = result + x
print('The Sum is : ', result)
sum(10,20,30,40)
sum(10,20,30)
sum(10,20)
sum(10)
sum()

Output:
The Sum is : 100
The Sum is : 60
The Sum is : 30
The Sum is : 10
The Sum is : 0

def sum(*n,name):
result =0
for x in n:
result = result + x
print("The Sum by", name, ": ", result)
sum(10,20,30,40,name = 'Robin')
sum(10,20,30,name = 'Rahul')
sum(10,20,name = 'Sachin')
sum(10,name = 'Sourav')
sum(name ='Karthi')

Output:
The Sum by Robin : 100
The Sum by Rahul : 60
The Sum by Sachin : 30
The Sum by Sourav : 10
The Sum by Karthi : 0

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


def f1(n1,*s): Description:
print(n1) - The function f1 takes one
for s1 in s: required positional argument n1
print(s1) and an arbitrary number of
f1(10) additional positional arguments
f1(10,20,30,40) collected in the tuple *s.
f1(10,"A",30,"B") - Inside the function, n1 is printed
first.
Output: - Then, the function iterates over
10 the elements in s and prints each
10 one.
20 - f1(10)
30 • n1 is 10.
40 • There are no additional
10 arguments in *s.
A
30 - f1(10, 20, 30, 40)
B • n1 is 10.
• The additional arguments 20,
30, and 40 are collected in *s.

- f1(10, "A", 30, "B")


• n1 is 10.

• The additional arguments "A",


30, and "B" are collected in *s.

We can call function by passing any number of keyword arguments

def f(arg1,arg2,arg3=4,arg4=8):
print(arg1,arg2,arg3,arg4)
f(3,2) #3 2 4 8
f(10,20,30,40) # 10,20,30,40
f(25,50,arg4=100) # 25 50 4 100
f(arg4=2,arg1=3,arg2=4) #3442

#f()
TypeError: f() missing 2 required positional arguments: 'arg1' and 'arg2'

• arg1 and arg2 are required positional arguments. These arguments


must be passed when the function is called.
• arg3 and arg4 have default values (4 and 8 respectively). These are
optional, and if not provided, the default values are used.

#f(arg3=10,arg4=20,30,40)
SyntaxError: positional argument follows keyword argument

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


In Python, positional arguments must always come before keyword
arguments when calling a function. To resolve this we may write: f(30, 40,
arg3=10, arg4=20)

#f(4,5,arg2=6)
TypeError: f() got multiple values for argument 'arg2'

#f(4,5,arg3=5,arg5=6)
TypeError: f() got an unexpected keyword argument 'arg5'

Output:
3248
10 20 30 40
25 50 4 100
3442

Types of Variables:
Python supports 2 types of variables.
1. Global Variables
2. Local Variables

1. Global Variables:
• The variables which are declared outside of function are called global
variables.
• These variables can be accessed in all functions of that module.
Consider the following example
a = 10 # Global Variables
def f1():
a = 20 # Local variable to the function 'f1'
print(a) # 20
def f2():
print(a) # 10
f1()
f2()

Output:
20
10

Suppose our requirement is, we don't want local variable. For that, one special
keyword is used, called as global.
global keyword: We can use global keyword for the following 2 purposes:
1. To declare global variables explicitly inside function.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


2. To make global variable available to the function so that we can perform
required modifications
a = 10 # Global Variables
def f1():
a = 777 # Local variable to the function 'f1'
print(a) #777
def f2():
print(a) # 10
f1()
f2()

Output:
777
10
a = 10 # Global Variables
def f1():
a = 20 # Local variable to the function 'f1'
print(a) # 20
def f2():
print(a) # 10
f1()
f2()

Output:
777
777

Description:
global a declares that a inside f1 refers to the global variable a.
a = 777 assigns the value 777 to the global variable a.

def f1():
x = 10 # local variable of 'f1()'
print(x)

def f2():

print(x) # local variable of 'f1()' can not accessed by function 'f2()'


f1()
f2()

Output:

10
NameError: name 'x' is not defined
def f1():
global x
x=10
print(x)

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


def f2():
print(x)
f1()
f2()

Output:

10
10
def f1():
global a
a = 888
print('f1 :',a)
def f2():
global a
a=999
print('f2 :',a)
f1()
f2()

Output:

f1 : 888
f2 : 999
def f1():
global a
a = 888
print('f1 :',a)
def f2():
global a
a=999
print('f2 :',a)
def f3():
print('f3 :',a)
f1()
f2()
f3()

Output:

f1 : 888
f2 : 999
f3 : 999
def f1():
global a
a = 888
print('f1 :',a)
def f2():
global a

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


a=999
print('f2 :',a)
f2()
f1()

Output:

f2 : 999
f1 : 888
If global variable and local variable having the same name, then we can
access global variable inside a function using globals() function.

a=10 #global variable


def f1():
a=777 #local variable
print(a)
print(globals()['a']) # globals() function cosisiting all global members
related
f1() # here, 'a' is the key value.

Output:

777
10

Illustrate *args and **kwargs parameters in Python programming language


with an example.

In Python, *args and **kwargs are used to pass a variable number of


arguments to a function.
• *args allows a function to accept any number of positional
arguments.
• **kwargs allows a function to accept any number of keyword
arguments.

Example 1:

def example_function(*args, **kwargs):


print("Positional arguments (*args):", args)
print("Keyword arguments (**kwargs):", kwargs)

# Calling the function


example_function(1, 2, 3, name="John", age=30)

Output:

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Positional arguments (*args): (1, 2, 3)
Keyword arguments (**kwargs): {'name': 'John', 'age': 30}

Explanation:
1. *args collects all the positional arguments (e.g., 1, 2, 3) into a tuple.
2. **kwargs collects all the keyword arguments (e.g., name="John",
age=30) into a dictionary.
We can use *args and **kwargs when we are not sure how many arguments
will be passed to the function.

Example 2:

def print_details(*args, **kwargs):


# Printing positional arguments
print("Positional Arguments:")
for arg in args:
print(arg)

# Printing keyword arguments


print("\nKeyword Arguments:")
for key, value in kwargs.items():
print(f"{key}: {value}")

# Calling the function


print_details("Apple", "Banana", "Orange", name="John", age=25, city="New
York")

Output:

Positional Arguments:
Apple
Banana
Orange

Keyword Arguments:
name: John
age: 25
city: New York

Recursive Functions
A function that calls itself is known as Recursive Function.
The main advantages of recursive functions are:
1. We can reduce length of the code and improves readability.
2. We can solve complex problems very easily. For example, Towers of Hanoi,
Ackerman's Problem etc.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Write a Python Function to find factorial of given number with
recursion.

def factorial(n):
if n==0:
result=1
else:
result=n*factorial(n-1)
return result
print("Factorial of 0 is :",factorial(0))
print("Factorial of 4 is :",factorial(4))
print("Factorial of 5 is :",factorial(5))
print("Factorial of 40 is :",factorial(40))

Output:

Factorial of 0 is : 1
Factorial of 4 is : 24
Factorial of 5 is : 120
Factorial of 40 is
815915283247897734345611269596115894272000000000

Anonymous Functions

• Sometimes we can declare a function without any name,such type of


nameless functions are called anonymous functions or lambda
functions.
• The main purpose of anonymous function is just for instant use(i.e., for
one time usage).
• A lambda function in Python is a small, anonymous function defined
with the keyword lambda rather than def.
• It allows you to create functions on the fly, typically for simple
operations, without needing to give the function a name.
• Lambda functions are often used for short, throwaway functions that
are used as arguments to higher-order functions like map(), filter(), and
reduce().
• Syntax of lambda function:
lambda arguments: expression
Example

# A regular function
def add(x, y):
return x + y

# Equivalent lambda function


add_lambda = lambda x, y: x + y

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


# Usage
print(add(3, 5)) # Output: 8
print(add_lambda(3, 5)) # Output: 8
# Using lambda with map to square each element in a list

numbers = [1, 2, 3, 4]
squared = list(map(lambda x: x ** 2, numbers))
print(squared) # Output: [1, 4, 9, 16]

# In this case, the lambda function is useful because it avoids


having to define a full function just for squaring numbers.

Lambda with reduce function (requires functools):

from functools import reduce

# This lambda function calculates the product of all elements in a list


numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product) # Output: 120

Lambda with filter function:


numbers = [11,20,31,40,51,60,71,80,91,100]
def even(x):
return x%2==0
evens = list(filter(even, numbers))
print("Even numbers:", evens)

numbers = [11,20,31,40,51,60,71,80,91,100]

evens = list(filter(lambda x:x%2 == 0, numbers))


print("Even numbers:", evens)

city = ['jaipur','kota','chandigarh','delhi','muzaffarpur']
def length(city):
return len(city)
sort = sorted(city, key=length)
print("sorted words by length:", sort)

Output:
['kota', 'delhi', 'jaipur', 'chandigarh', 'muzaffarpur']

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


city = ['jaipur','kota','chandigarh','delhi','muzaffarpur']

sort = sorted(city, key=lambda x:len(x))


print("sorted words by length:", sort)

Output:
sorted words by length: ['kota', 'delhi', 'jaipur', 'chandigarh',
'muzaffarpur']
If we want to print in reverse order then

city = ['jaipur','kota','chandigarh','delhi','muzaffarpur']

sort = sorted(city, key=lambda x:len(x),reverse=True)


print("sorted words by length:", sort)

Output:
sorted words by length: ['muzaffarpur', 'chandigarh', 'jaipur',
'delhi', 'kota']

OOPS in Python
To map with real world scenarios, we started using objects in code. This is
called object-oriented programming. Object-Oriented Programming (OOP) is
a programming paradigm that involves designing programs around concepts
represented as "objects". Python supports OOP through the provision of
classes.

Class and Object in Python


• Class: A collection of functions and attributes, attached to a
specific name, which represents an abstract concept.
• Attribute: A named piece of data (i.e. variable associated with a
class.
• Object: A single concrete instance generated from a class
#creating class
class Student: #class name starts with capital letter
name = "Kauleshwar Prasad" #name is a class attribute of the class Student
#creating object (instances)
s1 = Student()
print(s1.name)

s2 = Student()
print(s2.name)

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Output:

Kauleshwar Prasad
Kauleshwar Prasad
#creating class
class Car: #class name starts with capital letter
colour = "blue"
brand = "mercedes"
#creating object (instances)
s1 = Car()
print(s1.colour)
print(s1.brand)

output:
blue
mercedes

• To understand the meaning of classes we have to understand the built-


in __init__() function.
• All classes have a function called __init__(), which is always executed
when the class is being initiated.
• Use the __init__() function to assign values to object properties, or other
operations that are necessary to do when the object is being created.
Python Constructor

• A constructor is a special type of method (function) which is used to


initialize the instance members of the class.
• In C++ or Java, the constructor has the same name as its class, but it
treats the constructor differently in Python.
• It is used to create an object.

• Constructors can be of two types.


• Parameterized Constructor
• Non-parameterized Constructor

• Constructor definition is executed when we create the object of this


class.
• Constructors also verify that there are enough resources for the object
to perform any start-up task.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Creating the constructor in python

• In Python, the method the init () simulates the constructor of the


class.
• This method is called when the class is instantiated.
• It accepts the self-keyword as a first argument which allows
accessing the attributes or method of the class.
• We can pass any number of arguments at the time of creating the
class object, depending upon the init () definition.
• It is mostly used to initialize the class attributes.
• Every class must have a constructor, even if it simply relies on the
default constructor.

A non-parameterized constructor is a constructor that does not


take any arguments (except for self in Python). It initializes the
object with default values. Here’s an example:

class Person: In this example, the __init__


def __init__(self): constructor does not accept
self.name = "Unknown" any arguments, so when an
self.age = 0 object p1 is created, the default
values of "Unknown" and 0 are
p1 = Person() assigned to name and age
print(p1.name) # Output: respectively.
Unknown
print(p1.age) # Output: 0

A parameterized constructor in Python is a constructor that


accepts parameters to initialize an object with specific values.
Here's an example:
In this example, the __init__
constructor takes two
parameters, name and age,
allowing the object p1 to be
initialized with the values
"Kauleshwar" and 42.

We can technically use any


other word instead of self, but it
is highly discouraged and
uncommon. The first

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


class Person: parameter of instance methods
def __init__(self, name, age):
refers to the instance of the
self.name = name class itself, and while self is just
self.age = age a convention, it's important
because it makes the code more
# Creating an object and readable and understandable
passing arguments to the for other Python developers.
constructor
p1 = Person("Kauleshwar", 42)

# Accessing the object's


attributes
print(p1.name) # Output:
Kauleshwar
print(p1.age) # Output: 42

The __str__() Function


• The __str__() function in Python is a special method that
defines the string representation of an object.
• It allows us to customize how an object is displayed when
passed to print() or converted to a string using str().
• This method is especially useful for making objects more
readable by returning meaningful, human-friendly
information.
Why it's used:
1. Readability: Provides a clear representation of the object.
2. Customization: Allows you to control what is displayed when
the object is printed.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


The string representation of an object WITHOUT the __str__()
function:

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

p1 = Person("Kauleshwar", 42)

print(p1)

Output:

<__main__.Person object at 0x15039e602100>

The string representation of Description:


an object WITH the __str__() In this code, a class Person is
function: defined with an __init__ method
to initialize the name and age
class Person: attributes. Additionally, the
def __init__(self, name, age): __str__() method is overridden
self.name = name to provide a custom string
self.age = age representation of the object
when print() is called.
def __str__(self): • The __str__() method
return returns a formatted string
f"{self.name}({self.age})" "{self.name}({self.age})",
meaning when p1 is
p1 = Person("Kauleshwar", 42) printed, it outputs
"Kauleshwar(42)",
print(p1) combining the name and
age of the p1 object in a
Output: human-readable format.

Kauleshwar(42)

Object Methods
Objects can also contain methods. Methods in objects are
functions that belong to the object.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Let us create a method in the Person class:

Insert a function that prints a greeting, and execute it on the


p1 object:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def myfunc(self):
print("Hello my name is " + self.name)
print(f"My age is {self.age}" )

p1 = Person("Kauleshwar", 42)
p1.myfunc()

Output:

Hello my name is Kauleshwar


My age is 42

Note: The self parameter is a reference to the current instance


of the class, and is used to access variables that belong to the
class.
The self Parameter
The self parameter is a reference to the current instance of the
class, and is used to access variables that belongs to the class.
It does not have to be named self , you can call it whatever you
like, but it has to be the first parameter of any function in the class:
Use the words mysillyobject and abc instead of self:

class Person:
def __init__(mysillyobject, name, age):
mysillyobject.name = name
mysillyobject.age = age

def myfunc(abc):

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


print("Hello my name is " + abc.name)

p1 = Person("Kauleshwar", 42)
p1.myfunc()

Output:

Hello my name is Kauleshwar

Modify Object Properties


We can modify properties on objects like this:
Set the age of p1 to 40:
p1.age = 40
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

def myfunc(self):
print("Hello my name is " + self.name)

p1 = Person("Kauleshwar", 42)

p1.age = 40

print(p1.age)

Output:
40

Apart from __init__() and __str__(), there are several other special (or "magic")
methods in Python, commonly used to customize behavior in classes. Here
are a few:
1. __repr__(): Provides an unambiguous string representation, often used
for debugging.
2. __len__(): Returns the length of an object (used in len()).
3. __getitem__(): Allows indexing like lists or dictionaries.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


4. __setitem__(): Sets an item at a specific index.
5. __del__(): Defines the destructor of a class (called when an object is
deleted).
6. __eq__(), __lt__(): Used for comparison (==, <, etc.).

Python Inheritance
• Inheritance allows us to define a class that inherits all the methods and
properties from another class.
• Parent class is the class being inherited from, also called base class.
• Child class is the class that inherits from another class, also called derived
class.

# Parent class Description:


class Animal:
def __init__(self, name): In this example, the Dog class
self.name = name inherits from the Animal class but
overrides the speak() method.
def speak(self):
print(f"{self.name} makes a
sound.")

# Child class
class Dog(Animal):
def speak(self):
print(f"{self.name} barks.")

# Creating objects
a = Animal("Animal")
d = Dog("Dog")

a.speak() # Output: Animal makes


a sound.
d.speak() # Output: Dog barks.

An example of Python inheritance with two child ( Dog & Cat )of class
Animal:

# Parent class
class Animal:
def __init__(self, name):

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


self.name = name

def speak(self):
print(f"{self.name} makes a sound.")

# Child class 1
class Dog(Animal):
def speak(self):
print(f"{self.name} barks.")

# Child class 2
class Cat(Animal):
def speak(self):
print(f"{self.name} meows.")

# Creating objects
dog = Dog("Buddy")
cat = Cat("Whiskers")

dog.speak() # Output: Buddy barks.


cat.speak() # Output: Whiskers meows.

The Super() function


The super() function in Python is used to call a method from the
parent class. It is particularly useful in inheritance, allowing a
child class to access the methods or constructors of its parent
class without directly referring to the parent class by name.
class Animal:
def __init__(self, name):
self.name = name

class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name) # Calls the parent's constructor
self.breed = breed

dog = Dog("Buddy", "Golden Retriever")


print(dog.name) # Output: Buddy
print(dog.breed) # Output: Golden Retriever

Here super().__init__(name) calls the __init__() method of the


Animal class from the Dog class.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Multi-level inheritance
Multi-level inheritance in Python is when a class inherits from a
derived class, creating a chain of inheritance. It involves at least
three classes: the base (parent) class, the derived (child) class, and
the subclass (child of the derived class).

class Animal:
def sound(self):
print("Animal makes sound")

class Dog(Animal):
def bark(self):
print("Dog barks")

class Puppy(Dog):
def cute(self):
print("Puppy is cute")

p = Puppy()
p.sound() # Inherited from Animal
p.bark() # Inherited from Dog
p.cute() # Puppy method

Output:
Animal makes sound
Dog barks
Puppy is cute

Puppy inherits from Dog, which inherits from Animal.

Multiple inheritance
Multiple inheritance in Python allows a class to inherit attributes
and methods from more than one parent class. This enables a
class to acquire functionality from multiple sources.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


class Father: Description:
def traits(self):
print("Father: Tall") Parent Classes: Father and Mother are two
parent classes, each with a traits() method.

class Mother: • Father.traits() prints "Father: Tall".


def traits(self): • Mother.traits() prints "Mother:
Intelligent".
print("Mother: Intelligent")
Child Class: Child class inherits from both Father
class Child(Father, Mother): and Mother. This means Child will have access to
the attributes and methods of both parent
pass classes.
Method Resolution Order (MRO): When c.traits()
c = Child() is called, Python uses MRO to determine which
traits() method to invoke. Since Father is listed
c.traits() # Calls Father's traits first in the inheritance order, the Father.traits()
method due to Method Resolution method is called, and "Father: Tall" is printed.
Order (MRO)

Output:

Father: Tall

If you want to call both the Father and Mother methods, you can
explicitly call them using their class names in the child class
method, like this:

class Father:
def traits(self):
print("Father: Tall")

class Mother:
def traits(self):
print("Mother: Intelligent")

class Child(Father, Mother):


def traits(self):
Father.traits(self) # Call Father's method
Mother.traits(self) # Call Mother's method

c = Child()
c.traits() # Calls both Father and Mother's traits methods

Output:
Father: Tall
Mother: Intelligent

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Lets See difference between above 2 programs
class Father: class Father:
def traits(self): def traits(self):
print("Father: Tall")
print("Father: Tall")
class Mother:
def traits(self): class Mother:
print("Mother: Intelligent") def traits(self):
print("Mother: Intelligent")
class Child(Father, Mother):
pass
class Child(Father, Mother):
c = Child() def traits(self):
c.traits() # Calls Father's traits Father.traits(self) # Call
method due to Method Resolution Father's method
Order (MRO) Mother.traits(self) # Call
Mother's method

c = Child()
c.traits() # Calls both Father
and Mother's traits methods

What is the Diamond Problem in Python?


The "diamond problem" (also sometimes referred to as the "deadly diamond
of death") is an ambiguity that arises when a class inherits from two or more
classes that have one common superclass.
Here is the structure that creates the diamond problem:

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Class D inherits from both B and C, and B and C both inherit from A. Now, if
there is a method in A that B and C have overridden, and D does not override
it, then which version of the method does D inherit? Is it the one from B or C?
In other words, when a method is invoked in D that is defined in A, the
ambiguity is whether it should be invoked from B or C.
This is the diamond problem.
Python's Solution to the Diamond Problem
Python has a way to address this issue, it uses an approach called Method
Resolution Order (MRO), which follows the C3 linearization or just "C3
superclass linearization". This algorithm assures that a class always precedes
its parents and a parent precedes the other parent if it's derived from it.
Python also has super() function that helps with dynamic dispatching.
Here's a simple demonstration in code:
class A:
def display(self):
print("Display method from class A")

class B(A):
def display(self):
print("Display method from class B")

class C(A):
def display(self):
print("Display method from class C")

class D(B, C):


pass

d = D()
d.display()

Suppose we have classes A, B, C, and D. Class B and C inherit from A and


D inherits from both B and C. This creates a "diamond" shape of inheritance,
hence the "diamond problem". Let's suppose there's a method display() in
class A, which is overridden in classes B and C.
In this case, when you create an object of class D and call
the display() method, which display() method should it use? The one in
class B or the one in class C?
Python uses a method resolution order (MRO) algorithm that respects both
the order of classes in the inheritance list and their hierarchy. So, in the above
code, the output would be Display method from class B because B comes
before C in the inheritance list of class D.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


You can check the order using the mro() method:
print(D.mro())

Output:
[<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class
'__main__.A'>, <class 'object'>]

This means, when calling a method, Python will first look in class D, then B,
then C, then A, and finally, if nothing is found, in the built-in object class.
Solution using super():
To solve this, you can use super() inside the display method of the classes B
and C. The super() function ensures that Python follows the MRO and calls
methods in the correct order, avoiding ambiguity.
class A:
def display(self):
print("Display method from class A")

class B(A):
def display(self):
super().display() # Call the method from the next class in MRO
print("Display method from class B")

class C(A):
def display(self):
super().display() # Call the method from the next class in MRO
print("Display method from class C")

class D(B, C):


pass # Inherits display method without overriding it

d = D()
d.display()

# Check the method resolution order (MRO)


print(D.__mro__)

Output:

Display method from class A


Display method from class C
Display method from class B
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class
'__main__.A'>, <class 'object'>)

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Explanation:
• The super().display() calls the method from the next class in the MRO,
allowing the display methods from A, C, and B to be called in the
correct order.
• The MRO in this case is: D -> B -> C -> A, which means Python will
first look for methods in B, then C, then A.
Thus, by using super(), Python resolves the diamond problem and calls the
methods in the correct sequence based on the MRO.

Hierarchical inheritance
Hierarchical inheritance is a type of inheritance in object-oriented
programming where multiple derived classes inherit from a single base class.
In other words, it involves one parent class and many child classes that extend
the functionality of the parent.
# Base class
class Animal:
def speak(self):
print("This animal makes a sound")

# Derived class 1
class Dog(Animal):
def speak(self):
print("The dog barks")

# Derived class 2
class Cat(Animal):
def speak(self):
print("The cat meows")

# Derived class 3
class Cow(Animal):
def speak(self):
print("The cow moos")

# Objects of different derived classes


dog = Dog()
cat = Cat()
cow = Cow()

dog.speak() # Output: The dog barks


cat.speak() # Output: The cat meows
cow.speak() # Output: The cow moos

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


Output:

The dog barks


The cat meows
The cow moos

Description

In this example, the base class Animal is inherited by three derived classes:
Dog, Cat, and Cow. Each derived class overrides the speak() method,
demonstrating how hierarchical inheritance allows multiple child classes to
share the same parent while customizing or extending its functionality.

Encapsulation in Python
• Encapsulation is one of the fundamental concepts in object-oriented
programming (OOP).
• 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.
• To prevent accidental change, an object’s variable can only be changed by
an object’s method.
• Those types of variables are known as private variables.
• The goal of information hiding is to ensure that an object’s state is always
valid by controlling access to attributes that are hidden from the outside
world.
• This is also used to protect the object's internal state and ensure controlled
access via public methods (getters and setters).
• Encapsulation is achieved by defining variables as private or protected and
providing methods to access and modify them.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


class Employee:
def __init__(self, name, salary):
self.name = name # Public attribute
self.__salary = salary # Private attribute (encapsulated). The
__(double underscore ) makes it private.

# Getter method for private attribute


def get_salary(self):
return self.__salary

# Setter method to modify private attribute


def set_salary(self, new_salary):
if new_salary > 0:
self.__salary = new_salary
else:
print("Invalid salary amount!")

# Create an employee object


emp = Employee("John", 50000)

# Access public attribute


print(emp.name) # Output: John

# Accessing private attribute directly (This will cause an error)


# print(emp.__salary) # AttributeError: 'Employee' object has no attribute
'__salary'

# Accessing private attribute via getter


print(emp.get_salary()) # Output: 50000

# Modifying private attribute via setter


emp.set_salary(55000)
print(emp.get_salary()) # Output: 55000

# Trying to set an invalid salary


emp.set_salary(-1000) # Output: Invalid salary amount!

Output:

John
50000
55000
Invalid salary amount!

Description

• __salary: This is a private attribute, meaning it cannot be accessed


directly from outside the class. The __ (double underscore) makes it
private.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


• get_salary(): This is a getter method to access the private __salary
attribute.
• set_salary(): This is a setter method to modify the private __salary
attribute while validating the input.
Encapsulation helps to protect object integrity by controlling how the
attributes are accessed or modified.

Polymorphism in Python
• The literal meaning of polymorphism is the condition of occurrence in
different forms.
• Polymorphism is a very important concept in programming. It refers to the
use of a single type entity (method, operator or object) to represent different
types in different scenarios.
Let's take an example:
We know that the + operator is used extensively in Python programs. But, it
does not have a single usage.
For integer data types, + operator is used to perform arithmetic addition
operation.
num1 = 1
num2 = 2
print(num1+num2)

output
3

Similarly, for string data types, + operator is used to perform concatenation.


str1 = "Python"
str2 = "Programming"
print(str1+" "+str2)

Output
Python Programming

A single operator + has been used to carry out different operations


for distinct data types. This is one of the most simple occurrences
of polymorphism in Python.
Function Polymorphism in Python
There are some functions in Python which are compatible to run with multiple
data types.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg


One such function is the len() function. It can run with many data types in
Python. Let's look at some example use cases of the function.
print(len("Programiz"))
print(len(["Python", "Java", "C"]))
print(len({"Name": "John", "Address": "Nepal"}))

Output
9
3
2

Many data types such as string, list, tuple, set, and


dictionary can work with the len() function.

Prof. Kauleshwar Prasad, Department of CSE, Bhilai Institute of Technology, Durg

You might also like