Unit 3 Python
Unit 3 Python
Adding new data and functions is not Adding new data and function is
easy. easy.
Object-oriented programming is
Procedural programming is used for
used for designing large and
designing medium-sized programs.
complex programs.
PYTHON OOPS
In Python, object-oriented Programming (OOPs) is a programming paradigm that
uses objects and classes in programming. It aims to implement real-world
entities like inheritance, polymorphisms, encapsulation, etc. in the programming.
The main concept of OOPs is to bind the data and the functions that work
on that together as a single unit so that no other part of the code can
access this data.
OOPs Concepts in Python
Class
Objects
Polymorphism
Encapsulation
Inheritance
Data Abstraction
Python Class
A class is a collection of objects. A class contains the blueprints or the
prototype from which the objects are being created. It is a logical entity that
contains some attributes and methods.
To understand the need for creating a class let’s consider an example, let’s say you
wanted to track the number of dogs that may have different attributes like breed,
and age. If a list is used, the first element could be the dog’s breed while the second
element could represent its age. Let’s suppose there are 100 different dogs, then
how would you know which element is supposed to be which? What if you wanted to
add other properties to these dogs? This lacks organization and it’s the exact need
for classes.
Some points on Python class:
Classes are created by keyword class.
Attributes are the variables that belong to a class.
Attributes are always public and can be accessed using the dot (.) operator.
Eg.: Myclass.Myattribute
Class Definition Syntax:
class ClassName:
# Statement-1
.
.
.
# Statement-N
# demonstrate defining
# a class
class Dog:
pass
Python Objects
The object is an entity that has a state and behavior associated with it. It may
be any real-world object like a mouse, keyboard, chair, table, pen, etc. Integers,
strings, floating-point numbers, even arrays, and dictionaries, are all objects. More
specifically, any single integer or any single string is an object. The number 12 is an
object, the string “Hello, world” is an object, a list is an object that can hold other
objects, and so on. You’ve been using objects all along and may not even realize it.
An object consists of:
State: It is represented by the attributes of an object. It also reflects the
properties of an object.
Behavior: It is represented by the methods of an object. It also reflects the
response of an object to other objects.
Identity: It gives a unique name to an object and enables one object to
interact with other objects.
To understand the state, behavior, and identity let us take the example of the class
dog (explained above).
The identity can be considered as the name of the dog.
State or Attributes can be considered as the breed, age, or color of the dog.
The behavior can be considered as to whether the dog is eating or sleeping.
Creating an Object
This will create an object named obj of the class Dog defined above. Before diving
deep into objects and classes let us understand some basic keywords that will we
used while working with objects and classes.
obj = Dog()
# class attribute
attr1 = "mammal"
# Instance attribute
self.name = name
# Driver code
# Object instantiation
Rodger = Dog("Rodger")
Tommy = Dog("Tommy")
print("Rodger is a {}".format(Rodger.__class__.attr1))
Output
Rodger is a mammal
Tommy is also a mammal
My name is Rodger
My name is Tommy
attr1 = "mammal"
# Instance attribute
self.name = name
def speak(self):
# Driver code
# Object instantiation
Rodger = Dog("Rodger")
Tommy = Dog("Tommy")
Rodger.speak()
Tommy.speak()
Output
My name is Rodger
My name is Tommy
Types of Inheritance
Single Inheritance: Single-level inheritance enables a derived class to inherit
characteristics from a single-parent class.
Multilevel Inheritance: Multi-level inheritance enables a derived class to
inherit properties from an immediate parent class which in turn inherits properties
from his parent class.
Inheritance in Python
In the above article, we have created two classes i.e. Person (parent class) and
Employee (Child Class). The Employee class inherits from the Person class. We can
use the methods of the person class through the employee class as seen in the
display function in the above code. A child class can also modify the behavior of the
parent class as seen through the details() method.
# Python code to demonstrate how parent constructors are called.
# parent class
class Person(object):
self.name = name
self.idnumber = idnumber
def display(self):
print(self.name)
print(self.idnumber)
def details(self):
print("IdNumber: {}".format(self.idnumber))
# child class
class Employee(Person):
self.salary = salary
self.post = post
def details(self):
print("IdNumber: {}".format(self.idnumber))
print("Post: {}".format(self.post))
a.display()
a.details()
Output
Rahul
886012
My name is Rahul
IdNumber: 886012
Post: Intern
Python Polymorphism
Polymorphism simply means having many forms. For example, we need to
determine if the given species of birds fly or not, using polymorphism we can do this
using a single function.
Polymorphism in Python
This code demonstrates the concept of inheritance and method overriding in
Python classes. It shows how subclasses can override methods defined in their
parent class to provide specific behavior while still inheriting other methods from the
parent class.
class Bird:
def intro(self):
def flight(self):
class sparrow(Bird):
def flight(self):
class ostrich(Bird):
def flight(self):
obj_bird = Bird()
obj_spr = sparrow()
obj_ost = ostrich()
obj_bird.intro()
obj_bird.flight()
obj_spr.intro()
obj_spr.flight()
obj_ost.intro()
obj_ost.flight()
Output
There are many types of birds.
Most of the birds can fly but some cannot.
There are many types of birds.
Sparrows can fly.
There are many types of birds.
Ostriches cannot fly.
Data Abstraction
It hides unnecessary code details from the user. Also, when we do not want to
give out sensitive parts of our code implementation and this is where data
abstraction came.
Data Abstraction in Python can be achieved by creating abstract classes.
Python Encapsulation
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.
A class is an example of encapsulation as it encapsulates all the data that is member
functions, variables, etc.
Encapsulation in Python
In the above example, we have created the c variable as the private attribute. We
cannot even access this attribute directly and can’t even change its value.
# Python program to
class Base:
def __init__(self):
self.a = "GeeksforGeeks"
self.__c = "GeeksforGeeks"
class Derived(Base):
def __init__(self):
# Calling constructor of
# Base class
Base.__init__(self)
print(self.__c)
# Driver code
obj1 = Base()
print(obj1.a)
# raise an AttributeError
Output
GeeksforGeeks
INSTANCE VARIABLE / ATTRIBUTE
Instance attributes are those attributes that are not shared by objects. Every
object has its own copy of the instance attribute i.e. for every object, instance
attribute is different. Instance variables are unique to each instance of a
class. They are defined within methods and are prefixed with the self keyword.
These variables store data that is specific to an instance, making them essential
for object-oriented programming (OOP) principles like encapsulation. There are two
ways to access the instance variable of class:
Within the class by using self and object reference.
Using getattr() method
class student:
# constructor
# instance variable
self.name = name
self.rollno = rollno
def display(self):
# Driver Code
# object created
s = student('HARRY', 1001)
s.display()
print(s.name)
Output:
hello my name is: HARRY
my roll number is: 1001
HARRY
class emp:
name='Harsh'
salary='25000'
def show(self):
print(self.name)
print(self.salary)
# Driver Code
e1 = emp()
print(getattr(e1,'name'))
print(hasattr(e1,'name'))
# sets an attribute
setattr(e1,'height',152)
print(getattr(e1,'height'))
delattr(emp,'salary')
Output:
Harsh
True
152
CLASS VARIABLES
Class variables are shared among all instances of a class. They are defined
within the class but outside of any methods, typically near the top of the
class definition. Class variables store data that is common to all instances,
making them a powerful tool for managing shared state and settings.
class Dog:
species = "Canis familiaris" # Class variable
def __init__(self, name):
self.name = name # Instance variable
dog1 = Dog("Buddy")
dog2 = Dog("Milo")
print(dog1.species) # Output: "Canis familiaris"
print(dog2.species) # Output: "Canis familiaris"
It normally retains values as long as It normally retains values until the program
the object exists. terminates.
It has many copies so every object It has only one copy of the class variable
has its own personal copy of the so it is shared among different objects of the
instance variable. class.
These variables are declared without These variables are declared using the
using the static keyword. static keyword.
Changes that are made to these Changes that are made to these variables
variables through one object will not through one object will reflect in another
reflect in another object. object.
INSTANCE METHOD
An Instance Method is a function that works with a class instance. An
instance method can access and even modify the value of attributes of an
instance. The instance methods are bound to the class instance and perform a
set of actions on the data/value given by the object (instance) variables. If we use
the instance variable inside the methods, these methods are called
instance methods. It can modify the object state. It has one default
parameter :- self – It is a keyword which points to the current passed instance.
But it need not be passed every time while calling an instance method.
class shape:
# Calling Constructor
self.edge = edge
self.color = color
# Instance Method
def finEdges(self):
return self.edge
# Instance Method
self.edge = newedge
# Driver Code
square.modifyEdges(6)
Output
No. of edges for circle: 0
No. of edges for square: 6
ASSERT KEYWORD
Python Assertions in any programming language are the debugging tools that
help in the smooth flow of code. Assertions are mainly assumptions that a
programmer knows or always wants to be true and hence puts them in
code so that failure of these doesn’t allow the code to execute further.
Assert Keyword in Python
In simpler terms, we can say that assertion is the boolean expression that
checks if the statement is True or False. If the statement is true then it
does nothing and continues the execution, but if the statement is False
then it stops the execution of the program and raises an AssertionError
along with the optional message provided.
a = 4
b = 0
assert b != 0
print(a / b)
Output:
The value of a / b is :
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
Input In [19], in <cell line: 10>()
8 # using assert to check for 0
9 print("The value of a / b is : ")
---> 10 assert b != 0
11 print(a / b)
AssertionError:
# working of assert
# initializing number
a = 4
b = 0
print(a / b)
Output:
AssertionError: Zero Division Error
assert length > 0 and width > 0, "Length and width "+ "must be positive"
# Return statement
return area
area1 = calculate_rectangle_area(5, 6)
area2 = calculate_rectangle_area(-5, 6)
Output:
AssertionError: Length and width must be positive
Output:
x = 10
y = 20
a = "hello"
b = 42
print("a =", a)
print("b =", b)
Output:
a = hello
b = 42
# Initializing a dictionary
assert my_dict["apple"] == 1
assert my_dict["banana"] == 2
assert my_dict["cherry"] == 3
Output:
My dictionary contains the following key-value pairs:
{'apple': 1, 'banana': 2, 'cherry': 3}
Practical Application
This has a much greater utility in the Testing and Quality Assurance roles in any
development domain. Different types of assertions are used depending on the
application. Below is a simpler demonstration of a program that only allows only the
batch with all hot food to be dispatched, else rejects the whole batch.
# Python code to demonstrate working of assert Application
cut = 26
for i in batch:
Output:
40 is O.K.
26 is O.K.
39 is O.K.
30 is O.K.
Runtime Exception:
AssertionError: Batch is Rejected
def print_pi():
pi = 'inner pi variable'
print(pi)
print_pi()
print(pi)
Output:
inner pi variable
outer pi variable
The above program gives different outputs because the same variable
name pi resides in different namespaces, one inside the function print_pi and the
other in the upper level. When print_pi() gets executed, ‘inner pi variable‘ is printed
as that is pi value inside the function namespace. The value ‘outer pi variable‘ is
printed when pi is referenced in the outer namespace. From the above example, we
can guess that there definitely is a rule which is followed, in order in deciding from
which namespace a variable has to be picked.
# Local Scope
pi = 'global pi variable'
def inner():
pi = 'inner pi variable'
print(pi)
inner()
Output:
inner pi variable
On running the above program, the execution of the inner function prints the value
of its local(highest priority in LEGB rule) variable pi because it is defined and
available in the local scope.
pi = 'global pi variable'
def inner():
pi = 'inner pi variable'
print(pi)
inner()
print(pi)
Output:
inner pi variable
global pi variable
Therefore, as expected the program prints out the value in the local scope on the
execution of inner(). It is because it is defined inside the function and that is the first
place where the variable is looked up. The pi value in global scope is printed on the
execution of print(pi) on line 9.
def outer():
pi = 'outer pi variable'
def inner():
# pi = 'inner pi variable'
nonlocal pi
print(pi)
inner()
outer()
print(pi)
Output:
outer pi variable
global pi variable
When outer() is executed, inner() and consequently the print functions are executed,
which print the value the enclosed pi variable. Since pi is referred with
the nonlocal keyword, it means that pi needs to be accessed from
the outer function(i.e the outer scope). To summarize, the pi variable is not
found in local scope, so the higher scopes are looked up. It is found in both
enclosed and global scopes. But as per the LEGB hierarchy, the enclosed
scope variable is considered even though we have one defined in the global
scope.
def outer():
# pi = 'outer pi variable'
def inner():
# pi = 'inner pi variable'
print(pi)
inner()
outer()
Output:
3.141592653589793
Since, pi is not defined in either local, enclosed or global scope, the built-in scope is
looked up i.e the pi value imported from the math module. Since the program is able
to find the value of pi in the outermost scope, the following output is obtained.
PYTHON CLOSURES
Nested functions in Python
A function that is defined inside another function is known as a nested
function. Nested functions are able to access variables of the enclosing scope.
Definition
Python closure is a nested function that allows us to access variables of the
outer function even after the outer function is closed.They are used in
Decorators.
def greet(name):
# inner function
def display_name():
print("Hi", name)
# Output: Hi John
def innerFunction():
print(text)
if __name__ == '__main__':
myFunction = outerFunction('Hey!')
myFunction()
PYTHON ITERATORS
An Iterator in Python is an object that is used to iterate over iterable objects
like lists, tuples, dicts, and sets. The Python iterators object is initialized using
the iter() method. It uses the next() method for iteration.
1. __iter__(): The iter() method is called for the initialization of an iterator. This
returns an iterator object.
2. __next__(): The next method returns the next value for the iterable. When
we use a for loop to traverse any iterable object, internally it uses the iter()
method to get an iterator object, which further uses the next() method to
iterate over. This method raises a StopIteration to signal the end of the
iteration.
string = "GFG"
ch_iterator = iter(string)
print(next(ch_iterator))
print(next(ch_iterator))
print(next(ch_iterator))
Output :
G
F
G
print("Inside loop:")
# iterating on each item of the iterator object
for index, item in enumerate(tup_iter):
print(item)
Output:
Inside loop:
a
b
c
Outside loop:
d
e
PYTHON GENERATORS
A Generator in Python is a function that returns an iterator using
the Yield keyword.The yield keyword is used to produce a value
from the generator.
Generators use a yield statement in which the state of the
function is saved from the last call and can be picked up or
resumed the next time we call a generator function. Another great
advantage of the generator over a list is that it takes much less
memory.
def function_name():
yield statement
Generator Object
Python Generator functions return a generator object that is iterable, i.e.,
can be used as an Iterator. Generator objects are used either by calling the
next method of the generator object or using the generator object in
a “for in” loop.
Example:
In this example, we will create a simple generator function in Python to
generate objects using the next() function.
# A Python program to demonstrate use of
# generator object with next()
# A generator function
def simpleGeneratorFun():
yield 1
yield 2
yield 3
# x is a generator object
x = simpleGeneratorFun()
# In Python 3, __next__()
print(next(x))
print(next(x))
print(next(x))
Output:
1
2
3
Example:
In this example, we will create two generators for Fibonacci Numbers, first a
simple generator and second generator using a for loop.
# A simple generator for Fibonacci Numbers
def fib(limit):
# Initialize first two Fibonacci Numbers
a, b = 0, 1
# One by one yield next Fibonacci Number
while a < limit:
yield a
a, b = b, a + b
# Create a generator object
x = fib(5)
List1 = [1,2,3,4,5,6,7]
# List Comprehension
z = [x**3 for x in list1]
# Generator expression
a = (x**3 for x in list1)
print(z)
print(list(a))
Output:
[1, 8, 27, 64, 125, 216, 343]
[1, 8, 27, 64, 125, 216, 343]
DECORATORS IN PYTHON
Decorators are a very powerful and useful tool in Python since it allows programmers to modify the
behaviour of a function or class. Decorators allow us to wrap another function in order to extend
the behaviour of the wrapped function, without permanently modifying it.
First Class Objects
In Python, functions are first class objects which means that functions in Python can be used or
passed as arguments.
Properties of first class functions:
A function is an instance of the Object type.
You can store the function in a variable.
You can pass the function as a parameter to another function.
You can return the function from a function.
You can store them in data structures such as hash tables, lists,etc.