0% found this document useful (0 votes)
314 views88 pages

Unit - 4 Classes and Object-Oriented Programming

The document discusses object-oriented programming in Python. It defines key OOP concepts like classes, objects, methods, and variables. Classes provide blueprints for creating objects with attributes and behaviors. Objects are instances of classes that encapsulate state via attributes and expose behavior via methods. Instance variables store unique values for each object, while class variables are shared among all objects. The document also demonstrates how to define classes, create objects, and access variables and methods.

Uploaded by

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

Unit - 4 Classes and Object-Oriented Programming

The document discusses object-oriented programming in Python. It defines key OOP concepts like classes, objects, methods, and variables. Classes provide blueprints for creating objects with attributes and behaviors. Objects are instances of classes that encapsulate state via attributes and expose behavior via methods. Instance variables store unique values for each object, while class variables are shared among all objects. The document also demonstrates how to define classes, create objects, and access variables and methods.

Uploaded by

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

UNIT - 4

Classes and
Object-oriented
Programming
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.
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. 

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
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.
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.
The self  
Class methods must have an extra first parameter in
the method definition. We do not give a value for this
parameter when we call the method, Python provides
it
If we have a method that takes no arguments, then
we still have to have one argument.
This is similar to this pointer in C++ and this reference
in Java.
When we call a method of this object as
myobject.method(arg1, arg2), this is automatically
converted by Python into MyClass.method(myobject,
arg1, arg2) – this is all the special self is about.
The __init__ method 

The __init__ method is similar to constructors in C++


and Java.
It is run as soon as an object of a class is
instantiated.
The method is useful to do any initialization you
want to do with your object. 
Example 1: Creating a class and object with class and instance
attributes

class Dog:
 
    # class attribute
    attr1 = "mammal"
 
    # Instance attribute
    def __init__(self, name):
        self.name = name
Rodger = Dog("Rodger")
Tommy = Dog("Tommy")
# Accessing class attributes
print("Rodger is a {}".format(Rodger.__class__.attr1))
print("Tommy is also a {}".format(Tommy.__class__.attr1))
 
# Accessing instance attributes
print("My name is {}".format(Rodger.name))
print("My name is {}".format(Tommy.name))

Output
Rodger is a mammal
Tommy is also a mammal
My name is Rodger
My name is Tommy
Example 2: Creating Class and objects with methods
class Dog:
     # class attribute
    attr1 = "mammal"
     # Instance attribute
    def __init__(self, name):
        self.name = name
    def speak(self):
        print("My name is {}".format(self.name))
# Object instantiation
Rodger = Dog("Rodger")
Tommy = Dog("Tommy")
# Accessing class methods
Rodger.speak() Output
Tommy.speak() My name is Rodger
My name is Tommy
# define a class
class Bike:
name = ""
gear = 0
# create object of class
bike1 = Bike()
# access attributes and assign new values
bike1.gear = 11
bike1.name = "Mountain Bike"
print(f"Name: {bike1.name}, Gears: {bike1.gear} ")
# define a class
class Employee:
employee_id = 0
employee1 = Employee()
employee2 = Employee()
employee1.employeeID = 1001
print(f"Employee ID: {employee1.employeeID}")
employee2.employeeID = 1002
print(f"Employee ID: {employee2.employeeID}")
An Instance Variable in Python

If the value of a variable varies from object to object,


then such variables are called instance variables. For
every object, a separate copy of the instance variable
will be created.
Instance variables are not shared by objects. Every
object has its own copy of the instance attribute. This
means that for each object of a class, the instance
variable value is different.
When we create classes in Python, instance methods are
used regularly. we need to create an object to execute
the block of code or action defined in the instance
method.
Instance variables are used within the instance method.
We use the instance method to perform a set of actions
on the data/value provided by the instance variable.
We can access the instance variable using the object and
dot (.) operator.
In Python, to work with an instance variable and
method, we use the self keyword. We use
the self keyword as the first parameter to a method.
The self refers to the current object.
In the following example, we are creating two instance
variable name and age in the Student class.
class Student:
# constructor
def __init__(self, name, age):
self.name = name
self.age = age
s1 = Student("Jessa", 20)
print('Object 1')
print('Name:', s1.name)
print('Age:', s1.age)
s2= Student("Kelly", 10)
print('Object 2')
print('Name:', s2.name)
print('Age:', s2.age)
Instance methods: Used to access or modify the object
state. If we use instance variables inside a method,
such methods are called instance methods. It must
have a self parameter to refer to the current object.

Any method we create in a class will automatically be


created as an instance method unless we explicitly tell
Python that it is a class or static method.
Example :
class Student:
# constructor
def __init__(self, name, age):
# Instance variable
self.name = name
self.age = age

# instance method to access instance variable


def show(self):
print('Name:', self.name, 'Age:', self.age)
To call an instance method show() to access the student
object details such as name and age.

class Student:
# constructor
def __init__(self, name, age):
# Instance variable
self.name = name
self.age = age
# instance method access instance variable
def show(self):
print('Name:', self.name, 'Age:', self.age)
# create first object
print('First Student')
emma = Student("Jessa", 14)
# call instance method
emma.show()

# create second object


print('Second Student')
kelly = Student("Kelly", 16)
# call instance method O/P :
kelly.show() First Student
Name: Jessa Age: 14
Second Student
Name: Kelly Age: 16
Note:

•Inside any instance method, we can use self to access any


data or method that reside in our class. We are unable to
access it without a self parameter.

•An instance method can freely access attributes and even


modify the value of attributes of an object by using
the self parameter.

•By Using self.__class__ attribute we can access the class


attributes and change the class state. Therefore instance
method gives us control of changing the object as well as the
class state.
Class Variable in Python
If the value of a variable is not varied from object to
object, such types of variables are called class variables
or static variables.

Class variables are shared by all instances of a class.


Unlike instance variable, the value of a class variable is
not varied from object to object.

A class variable is declared inside of class, but outside


of any instance method or __init__() method.
class Student:
# Class variable
school_name = 'ABC School '

def __init__(self, name, roll_no):


self.name = name
self.roll_no = roll_no

# create first object


s1 = Student('Emma', 10)
print(s1.name, s1.roll_no, Student.school_name)
# access class variable

# create second object


s2 = Student('Jessa', 20)
# access class variable
print(s2.name, s2.roll_no, Student.school_name)
Example : Access Class Variable in the constructor

class Student:
# Class variable
school_name = 'ABC School ‘
# constructor
def __init__(self, name):
self.name = name
# access class variable inside constructor using self
print(self.school_name)
# access using class name
print(Student.school_name)
# create Object
s1 = Student('Emma')
Example : Access Class Variable in Instance method and
outside class

class Student:
# Class variable
school_name = 'ABC School ‘
# constructor
def __init__(self, name, roll_no):
self.name = name
self.roll_no = roll_no
# Instance method
def show(self):
print('Inside instance method')
# access using self
print(self.name, self.roll_no, self.school_name)
# access using class name
print(Student.school_name)
# create Object
s1 = Student('Emma', 10)
s1.show()
print('Outside class')
# access class variable outside class O/P
# access using object reference Inside instance method
print(s1.school_name) Emma 10 ABC School
ABC School
# access using class name Outside class
print(Student.school_name) ABC School
ABC School
Class Method 
Used to access or modify the class state. In method
implementation, if we use only class variables, then
such type of methods we should declare as a class
method. The class method has a cls parameter which
refers to the class.
A class method is bound to the class and not the
object of the class. It can access only class variables.
Class method works with the class since its parameter
is always the class itself.
The class method can be called both by the class and its
object.

Class.classmethod()
Or even
Class().classmethod()
But no matter what, the class method is always attached
to a class with the first argument as the class itself cls.
def classMethod(cls, args...)
Example : Create class method using classmethod()

class Person:
age = 25
def printAge(cls):
print('The age is:', cls.age)
# create printAge class method
Person.printAge = classmethod(Person.printAge)
Person.printAge()

O/P:
The age is: 25
Here, we have a class Person, with a member
variable age assigned to 25.
We also have a function printAge that takes a single
parameter cls and not self we usually take.
cls accepts the class Person as a parameter rather
than Person's object/instance.
Now, we pass the method Person.printAge as an
argument to the function classmethod. This converts
the method to a class method so that it accepts the
first parameter as a class (i.e. Person).
In the final line, we call printAge without creating a
Person object like we do for static methods. This prints
the class variable age.
Example : ( simple class method)
class bscit:
    course = 'DSA'
 
    def purchase(obj):
        print("Purchase course : ", obj.course)
 
 
bscit.purchase = classmethod(bscit.purchase)
bscit.purchase()

Output:
Purchase course : DSA
Create class method using classmethod() :
class Student:
     # create a variable
    name = “Pythonfor python"
     # create a function
    def print_name(obj):
        print("The name is : ", obj.name)
 # create print_name classmethod
# before creating this line print_name()
# It can be called only with object not with class
Student.print_name = classmethod(Student.print_name)
 # now this method can be called as classmethod
# print_name() method is called a class method
Student.print_name()
Static Methods in Python
A static method is a general utility method that
performs a task in isolation. Static methods in Python
are similar to those found in Java or C++.
A static method is bound to the class and not the object
of the class. Therefore, we can call it using the class
name.
A static method doesn’t have access to the class and 
instance variables because it does not receive an implicit
first argument like self and cls. Therefore it cannot
modify the state of the object or class.
The class method can be called
using ClassName.method_name() as well as by using an
object of the class.
Example :
class Employee:
@staticmethod
def sample(x):
print('Inside static method', x)
# call static method
Employee.sample(10)
# can be called using object
emp = Employee() emp.sample(10)
Example :
class Calculator:
def addNumbers(x, y):
return x + y
# create addNumbers static method
Calculator.addNumbers = staticmethod(Calculator.addNumbers)
print('Product:', Calculator.addNumbers(15, 110))

Note that we called the addNumbers we created without an


object. When we run this program, here is the output we will
get:

Product: 125
Private Variables and Methods :
Private members in programming languages (such as 
C++ and Java) are the members that are only available
to their own class. Neither the subclasses nor the
classes in the same package have access to them.
In Python, there is no such strict enforcement of
privacy. It does not have access specifiers like other
languages to make variables or methods private to the
class.
However, we can use the name mangling feature of
Python to make a variable or method appear as
restricted.
We have to prefix the __ (double underscore) in the name of
the variable to represent it as a private variable.
class BankAccount:
def __init__(self, balance):
self.__balance = balance
#private variable
def displayBalance(self):
print("Balance: ", self.__balance)
myaccount = BankAccount('85,33,999')
myaccount.displayBalance()
print(myaccount.__balance)
#outputs 'Balance: 85,33,999'
#AttributeError: object has no attribute '__balance‘
Accessing the variable __balance through object throws an error because Python has
scrambled its name to _BankAccount__balance (class name with leading underscore
followed by the name of the variable).
Python does it so to ensure that subclasses don’t
accidentally override the private attributes of the 
parent class.
If we replace the name with _BankAccount__balance in
the print statement, then it works fine.

print(myaccount._BankAccount__balance)
#outputs '85,33,999‘

The same concept is applicable to methods also. Prefixing


the __ in the name of the member methods
(__displayBalance),
mangles its name (_BankAccount__displayBalance).
class BankAccount:
def __init__(self, balance):
self.__balance = balance #private variable
#private method
def __displayBalance(self):
print("Balance: ", self.__balance)

myaccount = BankAccount('85,33,999')
myaccount.__displayBalance()

#AttributeError: object has no attribute '__displayBalance'


In the end, we can conclude that Python has no strict
mechanism to make variables and methods private.

 Although, we can prefix __ (double underscore) in the


name as to enforce the name mangling, the attributes
can still be accessed by the other classes.
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.
Syntax :

# define a superclass
class super_class:
# attributes and method definition

# inheritance class
sub_class(super_class):
# attributes and method of super_class
# attributes and method of sub_class
# inherit from Animal
class Dog(Animal):
Example : # new method in subclass
class Animal: def display(self):
# access name attribute of
# attribute and method superclass using self
of the parent class print("My name is ", self.name)
name = ""
# create an object of the subclass
labrador = Dog()
def eat(self):
print("I can eat") # access superclass attribute and
method
labrador.name = "Rohu"
labrador.eat()
Output
I can eat # call subclass method
My name is Rohu labrador.display()
Example :
class Polygon:
# Initializing the number of sides
def __init__(self, no_of_sides):
self.n = no_of_sides
self.sides = [0 for i in range(no_of_sides)]

def inputSides(self):
self.sides = [float(input("Enter side "+str(i+1)+" : ")) for i
in range(self.n)]

# method to display the length of each side of the polygon


def dispSides(self):
for i in range(self.n):
print("Side",i+1,"is",self.sides[i])
class Triangle(Polygon):
# Initializing the number of sides of the triangle to 3 by
# calling the __init__ method of the Polygon class
def __init__(self):
Polygon.__init__(self,3)

def findArea(self):
a, b, c = self.sides

# calculate the semi-perimeter


s = (a + b + c) / 2
# Using Heron's formula to calculate the area of the
triangle
area = (s*(s-a)*(s-b)*(s-c)) ** 0.5
print('The area of the triangle is %0.2f' %area)

# Creating an instance of the Triangle class


t = Triangle()

# Prompting the user to enter the sides of the triangle


t.inputSides()

# Displaying the sides of the triangle


t.dispSides()

# Calculating and printing the area of the triangle


t.findArea()
O/P :

Enter side 1 : 3
Enter side 2 : 5
Enter side 3 : 4
Side 1 is 3.0
Side 2 is 5.0
Side 3 is 4.0
The area of the triangle is 6.00
Namespaces in Python

A namespace is a collection of currently defined


symbolic names along with information about the
object that each name references.

You can think of a namespace as a dictionary in


which the keys are the object names and the values
are the objects themselves.

Each key-value pair maps a name to its


corresponding object.
 In a Python program, there are four types of
namespaces:
1. Built-In
2. Global
3. Enclosing
4. Local

These have differing lifetimes. As Python executes a


program, it creates namespaces as necessary and deletes
them when they’re no longer needed. Typically, many
namespaces will exist at any given time.
>>> a=1
>>> def func1():
b=2
def func2():
c=3
In this code, ‘a’ is in the global namespace in
python. ‘b’ is in the local namespace of func1,
and ‘c’ is in the nested local python namespace of
func2.
To func2, ‘c’ is local, ‘b’ is nonlocal, and ‘a’ is
global. By nonlocal, we mean it isn’t global, but
isn’t local either.
# global_var is in the global namespace
global_var = 10
def outer_function():
# outer_var is in the local namespace
outer_var = 20
def inner_function():
# inner_var is in the nested local namespace
inner_var = 30
print(inner_var)
print(outer_var)
inner_function()
# print the value of the global variable
print(global_var)
# call the outer function and print local and nested local
variables
outer_function()
Destructors in Python
Destructors are called when an object gets destroyed. In
Python, destructors are not needed as much as in C++
because Python has a garbage collector that handles
memory management automatically. 
The __del__() method is a known as a destructor method
in Python. It is called when all references to the object
have been deleted i.e when an object is garbage
collected. 
Syntax of destructor declaration : 
 def __del__(self):
# body of destructor

Note : A reference to objects is also deleted when the


object goes out of reference or when the program ends. 
# Python program to illustrate destructor
class Employee:
 
    # Initializing
    def __init__(self):
        print('Employee created.')
 
    # Deleting (Calling destructor)
    def __del__(self):
        print('Destructor called, Employee deleted.')
 
obj = Employee()
del obj Output :
Employee created.
Destructor called, Employee deleted.
Note : The destructor was called after the program
ended or when all the references to object are deleted
i.e when the reference count becomes zero, not when
object went out of scope.

 This example gives the explanation of above-


mentioned note. Here, notice that the destructor is
called after the ‘Program End…’ printed.
# Python program to illustrate destructor
  class Employee:
      # Initializing
   def __init__(self):
         print('Employee created')
       # Calling destructor
   def __del__(self):
         print("Destructor called")
  def Create_obj(): Output
   print('Making Object...') Calling Create_obj() function...
Making Object...
     obj = Employee() Employee created
     print('function end...') function end...
Program End...
     return obj Destructor called
 print('Calling Create_obj() function...')
obj = Create_obj()
print('Program End...')
Memory Management in Python

Memory Is an Empty Book


You can begin by thinking of a computer’s memory as an
empty book intended for short stories. There’s nothing
written on the pages yet. Eventually, different authors will
come along. Each author wants some space to write their
story in.
Since they aren’t allowed to write over each other, they
must be careful about which pages they write in. Before
they begin writing, they consult the manager of the book.
The manager then decides where in the book they’re
allowed to write.
Since this book is around for a long time, many of the
stories in it are no longer relevant. When no one reads or
references the stories, they are removed to make room
for new stories.
In essence, computer memory is like that empty book. In
fact, it’s common to call fixed-length contiguous blocks of
memory pages, so this analogy holds pretty well.
The authors are like different applications or processes
that need to store data in memory. The manager, who
decides where the authors can write in the book, plays
the role of a memory manager of sorts. The person who
removed the old stories to make room for new ones is a
garbage collector.
Garbage Collection
Garbage collection is a process in which the interpreter
frees up the memory when not in use to make it
available for other objects.
Assume a case where no reference is pointing to an
object in memory i.e. it is not in use so, the virtual
machine has a garbage collector that automatically
deletes that object from the heap memory.
Heap space in Python primarily maintains all objects
and data structures, whereas its counterpart, stack
space, contains all the references to the objects in heap
space. When an object is updated, the new value is
written at a new location and the variable is referenced
to the new address.
Reference Counting
Reference counting works by counting the number of times an
object is referenced by other objects in the system. When
references to an object are removed, the reference count for an
object is decremented. When the reference count becomes zero,
the object is deallocated.
For example, Let’s suppose there are two or more variables that
have the same value, so, what Python virtual machine does is,
rather than creating another object of the same value in the
private heap, it actually makes the second variable point to that
originally existing value in the private heap. Therefore, in the case
of classes, having a number of references may occupy a large
amount of space in the memory, in such a case referencing
counting is highly beneficial to preserve the memory to be
available for other objects
Example:
x = 10
When x = 10 is executed an integer object 10 is created
in memory and its reference is assigned to variable x, this
is because everything is object in Python.
Let’s verify if it’s true
x = 10
y=x
  
if id(x) == id(y):
    print("x and y refer to the same object")

Output:
x and y refer to the same object

In the above example, y = x will create another reference


variable y which will refer to the same object because Python
optimizes memory utilization by allocation the same object
reference to a new variable if the object already exists with
the same value.
Now, let’s change the value of x and see what happens.
x = 10
y=x
x += 1
  
if id(x) != id(y):
    print("x and y do not refer to the same object")
Output:
x and y do not refer to the same object
Objects in Python
Everything in Python is an object. Classes, functions,
and even simple data types, such as integers, floats,
and strings, are objects in Python. When we define
an integer in Python, CPython internally creates an
object of type integer. These objects are stored in
heap memory.
Each Python object consists of three fields:
1. Value
2. Type
3. Reference count
Let's consider a simple example:
a = 100
When the above code is executed, CPython creates an
object of type integer and allocates memory for this object
on the heap memory.
The type indicates the type of the object in CPython, and
the value field, as the name suggests, stores the value of
the object (100 in this case).

Variables in Python
Variables in Python are just references to the actual object
in memory. They are like names or labels that point to the
actual object in memory. They do not store any value.
Consider the following example:
a = 100
As discussed earlier, when the above code is executed, CPython
internally creates an object of type integer. The
variable a points to this integer object as shown below:
We can access the integer object in the Python program using
the variable a.
Let's assign this integer object to another variable b:
b=a
When the above code is executed, the variables a and b both
point to the same integer object, as shown below:
Let's now increment the value of the integer object by 1:
# Increment a by 1 a = a + 1
When the above code is executed, CPython creates a
new integer object with the value 101 and makes
variable a point to this new integer object. Variable b will
continue to point to the integer object with the
value 100, as shown below:
Python Multiple Inheritance
A class can be derived from more than one superclass in
Python. This is called multiple inheritance.
Syntax :

class SuperClass1:
# features of SuperClass1
class SuperClass2:
# features of SuperClass2 class MultiDerived(SuperClass1,
SuperClass2):
# features of SuperClass1 + SuperClass2 + MultiDerived class
class Mammal:
def mammal_info(self):
print("Mammals can give direct birth.") class
WingedAnimal:
def winged_animal_info(self):
print("Winged animals can flap.")
class Bat(Mammal, WingedAnimal):
pass
# create an object of Bat class
b1 = Bat()
b1.mammal_info()
b1.winged_animal_info()
Output
Mammals can give direct birth.
Winged animals can flap.
Python Operator Overloading

In Python, we can change the way operators work for


user-defined types.
For example, the + operator will perform arithmetic
addition on two numbers, merge two lists, or
concatenate two strings.
This feature in Python that allows the same operator
to have different meaning according to the context is
called operator overloading.
Example :

# Python program to show use of


# + operator for different purposes.
 
print(1 + 2)
 
# concatenate two strings
print(“Python"+"For")
  Output
# Product two numbers 3
print(3 * 4) PythonFor
  12
# Repeat the String
print(“Python"*4) PythonPyhtonPythonPython
Special Functions
Class functions that begin with double
underscore __ are called special functions in Python.
The special functions are defined by the Python
interpreter and used to implement certain features or
behaviors.
They are called "double underscore" functions
because they have a double underscore prefix and
suffix, such as __init__() or __add__().
Here are some of the special functions available in
Python,
Function Description

__init__() initialize the attributes of


the object
returns a string
__str__() representation of the
object

__len__() returns the length of the


object
__add__() adds two objects

__call__() call objects of the class


like a normal function
How to overload the operators in Python?

Consider that we have two objects which are a physical


representation of a class (user-defined data type) and we have
to add two objects with binary ‘+’ operator it throws an error,
because compiler don’t know how to add two objects.
So we define a method for an operator and that process is
called operator overloading.
We can overload all existing operators but we can’t create a
new operator.
To perform operator overloading, Python provides some
special function or magic function that is automatically
invoked when it is associated with that particular operator.  
# Python Program to perform addition
# of two complex numbers using binary
# + operator overloading.
 class complex:
    def __init__(self, a, b):
        self.a = a
        self.b = b
 
     # adding two objects
    def __add__(self, other):
        return self.a + other.a, self.b + other.b
 
Ob1 = complex(1, 2)
Ob2 = complex(2, 3) Output
Ob3 = Ob1 + Ob2
print(Ob3)
(3, 5)
# Python program to overload
# a comparison operators
 class A:
    def __init__(self, a):
        self.a = a
    def __gt__(self, other):
        if(self.a>other.a):
            return True
        else:
            return False
ob1 = A(2)
ob2 = A(3)
Output:
if(ob1>ob2):
ob2 is greater than ob1
    print("ob1 is greater than ob2")
else:
    print("ob2 is greater than ob1")
UNIT 4
COMPLETED

You might also like