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

Unit 3 Class and Objects

Using for college students (MCA pg students)

Uploaded by

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

Unit 3 Class and Objects

Using for college students (MCA pg students)

Uploaded by

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

Unit III

Class Definition - Creating Objects - Built-in Attribute Methods - Built-in Class Attributes -
Destructors in Python - Encapsulation - Data Hiding - Inheritance - Method Overriding Polymorphism.

Class Definition
Classes provide a means of bundling data and functionality together.
Creating a new class creates a new type of object, allowing new instances of that type to be made. Each
class instance can have attributes attached to it for maintaining its state. Class instances can also have
methods for modifying its state.
 Classes and instances are related to each other.
 Classes provide the definition of an object.
 Instances are the objects specified in the class definition.
Compared with other programming languages, Python’s class mechanism adds classes with a minimum
of new syntax and semantics. It is a mixture of the class mechanisms found in C++ and Modula-3.
Python classes provide all the standard features of Object Oriented Programming:
 Class inheritance mechanism allows multiple base classes, a derived class can override any
methods of its base class or classes, and a method can call the method of a base class with the
same name.
 Objects can contain arbitrary amounts and kinds of data.
 Classes share the dynamic nature of Python: they are created at runtime, and can be modified
further after creation.

Class Definition

The simplest form of class definition looks like this:


class ClassName:
<statement-1>
.
.
<statement-N>

Class definitions, like function definitions (def statements) must be executed before they have any
effect.

The statements inside a class definition will usually be function definitions, but other statements are
allowed. The function definitions inside a class have a peculiar form of argument list, dictated by the
calling conventions for methods.

When a class definition is entered, a new namespace is created, and used as the local scope, thus, all
assignments to local variables go into this new namespace. In particular, function definitions bind the
name of the new function here.
Create a Class
To create a class, use the keyword class:
class MyClass:
x=5
print(MyClass)

Output: <class ‘__main__.MyClass’>

Class Objects
Create Object

Now we can use the class named MyClass to create objects:

Create an object named p1, and print the value of x:


p1 = MyClass()
print(p1.x)
Output: 5

Class objects support two kinds of operations:

 Attribute references and


 Instantiation.

Attribute references use the standard syntax used for all attribute references in Python: obj.name. Valid
attribute names are all the names that were in the class’s namespace when the class object was created.
So, if the class definition looked like this:
class MyClass:
"""A simple example class"""
i = 12345

def f(self):
return 'hello world'

then MyClass.i and MyClass.f are valid attribute references, returning an integer and a function object,
respectively.

Class attributes can also be assigned to, so you can change the value of MyClass.i by assignment.

Class instantiation uses function notation. Just pretend that the class object is a parameter-less function
that returns a new instance of the class. For example (assuming the above class):
x = MyClass()

creates a new instance of the class and assigns this object to the local variable x.

The instantiation operation (“calling” a class object) creates an empty object.


The __init__() Function

All classes have a function called __init__(), which is always executed when the class is being initiated.

__init__() function is used to assign values to the object properties, or other operations that are
necessary to do when the object is being created.

Many classes like to create objects with instances customized to a specific initial state. Therefore a
class may define a special method named __init__(), like this:
def __init__(self):
self.data = []

When a class defines an __init__() method, class instantiation automatically invokes __init__() for the
newly created class instance. So in this example, a new, initialized instance can be obtained by:
x = MyClass()
Of course, the __init__() method may have arguments for greater flexibility. In that case, arguments
given to the class instantiation operator are passed on to __init__().

Create a class named Person, use the __init__() function to assign values for name and age:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age

p1 = Person("John", 36)
print(p1.name)
print(p1.age)

Another example,
>>> class Complex:
def __init__(self, realpart, imagpart):
self.r = realpart
self.i = imagpart

>>> x = Complex(3.0, -4.5)


>>> x.r, x.i
(3.0, -4.5)
The __init__() function is called automatically every time the class is being used to create a new object.
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):
print("Hello my name is " + abc.name)

p1 = Person("John", 36)
p1.myfunc()
Output: Hello my name is John
The __str__() Function

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

If the __str__() function is not set, the string representation of the object is returned:

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("John", 36)
print(p1)
Output: <__main__.Person object at 0x15039e602100>

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


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

def __str__(self):
return f"{self.name}({self.age})"

p1 = Person("John", 36)
print(p1)

Output: John(36)

Object Methods

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

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): #method


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

p1 = Person("John", 36)
p1.myfunc()
Output: Hello my name is John

Modify Object Properties

You can modify properties on objects like this:

Set the age of p1 to 40:


p1.age = 40
print(p1.age)
Output: 40
Delete Object Properties

You can delete properties on objects by using the del keyword:

Delete the age property from the p1 object:


del p1.age
Output:
After if you try to print it, it will throw an error stating
Traceback (most recent call last):
File "./prog.py", line 12, in <module>
AttributeError: 'Person' object has no attribute 'age'

Delete Objects

You can delete objects by using the del keyword:

Delete the p1 object:


del p1
Output:
If you try to print the object it will throw the following error.
Traceback (most recent call last):
File "demo_class8.py", line 13, in <module>
print(p1)
NameError: 'p1' is not defined

The pass Statement

class definitions cannot be empty, but if for some reason have a class definition with no content, put in
the pass statement to avoid getting an error.
class Person:
pass

Class and Instance Variables

 Instance variables are for data, unique to each instance and class variables are for
attributes and methods shared by all instances of the class.
 Instance variables are variables whose value is assigned inside a constructor or method
with self, whereas class variables are variables whose value is assigned in the class.
Defining instance variables using the normal method:

class Dog:
# Class Variable
animal = 'dog'

# The init method or constructor


def __init__(self, breed):

# Instance Variable
self.breed = breed

# Adds an instance variable


def setColor(self, color):
self.color = color

# Retrieves instance variable


def getColor(self):
return self.color

# Driver Code
Rodger = Dog("pug")
Rodger.setColor("brown")
print(Rodger.getColor())

Output:
brown
Defining instance variables using a constructor.

class Dog:
# Class Variable
animal = 'dog'

# The init method or constructor


def __init__(self, breed, color):

# Instance Variable
self.breed = breed
self.color = color

# Objects of Dog class


Rodger = Dog("Pug", "brown")
Buzo = Dog("Bulldog", "black")

print('Rodger details:')
print('Rodger is a', Rodger.animal)
print('Breed: ', Rodger.breed)
print('Color: ', Rodger.color)

print('\nBuzo details:')
print('Buzo is a', Buzo.animal)
print('Breed: ', Buzo.breed)
print('Color: ', Buzo.color)

# Class variables can be accessed using class name also


print("\nAccessing class variable using class name")
print(Dog.animal)

Output:
Rodger details:
Rodger is a dog
Breed: Pug
Color: brown
Buzo details:
Buzo is a dog
Breed: Bulldog
Color: black
Accessing class variable using class name
dog
Built-in Attribute Methods

 Attributes of a class are function objects that define corresponding methods of its instances.
 They are used to implement access controls of the classes.

Attributes of a class can also be accessed using the following built-in methods and functions :
1. getattr() – This function is used to access the attribute of object.
2. hasattr() – This function is used to check if an attribute exist or not.
3. setattr() – This function is used to set an attribute. If the attribute does not exist, then it would be
created.
4. delattr() – This function is used to delete an attribute. If you are accessing the attribute after
deleting it raises error “class has no attribute”.

The following methods are explained with the example given below:

# Python code for accessing attributes of class

class emp:
name='Harish'
salary='25000'

def show(self):
print (self.name)
print (self.salary)

e1 = emp()
# Use getattr instead of e1.name
print (getattr(e1,'name'))

# returns true if object has attribute


print (hasattr(e1,'name'))

# sets an attribute
setattr(e1,'height',152)

# returns the value of attribute name height


print (getattr(e1,'height'))

# delete the attribute


delattr(emp,'salary')

Output :
Harish
True
152
Static methods: A static method is a method [member function] that don’t use argument self at all.
To declare a static method, proceed it with the statement “@staticmethod”.

# Python code for accessing methods using static method

class test:
@staticmethod

def square(x):
test.result = x*x

# object 1 for class


t1=test()
# object 2 for class
t2 = test()
t1.square(2)
print (t1.result)

t2.square(3)
print (t2.result)

# printing the last value of result as we declared the method static


print (t1.result)

Output :
4
9
9

Accessing attributes and methods of one class in another class

Accessing attributes and methods of one class in another class is done by passing the object of one
class to another.

class ClassA():
def __init__(self):
self.var1 = 1
self.var2 = 2
def methodA(self):
self.var1 = self.var1 + self.var2
return self.var1

class ClassB(ClassA):
def __init__(self, class_a):
self.var1 = class_a.var1
self.var2 = class_a.var2
object1 = ClassA()
# updates the value of var1
summ = object1.methodA()

# return the value of var1


print (summ)

# passes object of classA


object2 = ClassB(object1)

# return the values carried by var1,var2


print( object2.var1)
print (object2.var2)

Output : 3 3 2

Built-In Class Attributes


 The built-in class attributes provides information about the class.
 Using the dot (.) operator, we may access the built-in class attributes.

The built-in class attributes in python are listed below –

Attributes Description

__dict__ Dictionary containing the class namespace

__doc__ If there is a class documentation class, this returns it. Otherwise, None

__name__ Class name.

__module__ Module name in which the class is defined. This attribute is "__main__" in
interactive mode.

__bases__ A possibly empty tuple containing the base classes, in the order of their
occurrence in the base class list.

Example 1: __dict__ Class Attribute


Here we are printing the dict of the class.
# creating a class Sample
class Sample:
'Welcome to Python'
def __init__(self):
print("The __init__ method")
# Printing the value of Built-in __dict__ attribute for the Sample class
print(Sample.__dict__)
Output
On executing, the above program will generate the following output −
{'__module__': '__main__', '__doc__': 'Welcome to Python', '__init__': <function Sample.__init__ at
0x7fd7812df050>, '__dict__': <attribute '__dict__' of ' Sample ' objects>, '__weakref__': <attribute
'__weakref__' of ' Sample ' objects>}

Example 2: __doc__ Class Attribute


In the Python code below, we create a Sample class with documentation.
# creating a class Sample
class Sample:
'Welcome to Python'
def __init__(self):
print("The __init__ method")
# getting the documentation for the Sample using built-in __doc__ class attribute
print(Sample.__doc__)

Output
Welcome to Python

Example 3: __name__ Class Attribute


In Python, we print the name of the class using the __name__ class attribute
class Sample:
'Welcome to Python'
def __init__(self):
print("The __init__ method")
# getting the name of the class using built-in __name__ class attribute
print(Sample.__name__)

Output
Sample

Example 4: __module__ Class Attribute


In Python, we print the module of the class using the __module__ class attribute
class Sample:
'Welcome to Python'
def __init__(self):
print("The __init__ method")
# getting the module of the class using built-in __module__ class attribute
print(Sample.__module__ )

Output
__main__

Example 5: __bases__ Class Attribute


In the Python code below, we print the bases of the class using the built-in __bases__ class attribute
class Sample:
'Welcome to Python'
def __init__(self):
print("The __init__ method")
# getting the bases of the class using built-in __bases__ class attribute
print(Sample.__bases__)

Output
(<class 'object'>,)

Applying all the class attributes in a single program

Following are the Algorithm to be followed to perform the desired task −


 Create a parent class with the name Employee
 Create a variable and initiaize the variable with 0 for storing the employ count.
 Create an init constructor which accepts the name, and salary as arguments.
In Python, __init__ is one of the reserved methods. It is referred to as a constructor in object-
oriented programming. When an object is created from the class and access is necessary to
initialize the class's attributes, the __init__ function is called.
 Initialize the values of instance attributes
 Increment the employ count by 1
 Create a function with the name displayCount which prints the total employee count.
 Print the employ count by applying the (.) operator on the Employee class
 Create another function with the name displayEmployee and print name and salary.
 Create a child class with the name subEmployee which inherits properties from the parent
Employee class
 Use __doc__ class attribute, to print the Employee class with documentation
 Use __name__ class attribute, to print the name of the Employee class
 Use the __module__ class attribute, to print the module of the Employee class
 Use __dict__ class attribute, to print the dictionary containing the Employee class namespace
 Use the __bases__ class attribute, to print all the base classes for the derived class.
Example
# Creating a parent class with the name Employee
class Employee:
'Common base class for all employees'
# initiaizing the variable with 0 for storing the employ count
empCount = 0

# init constructor which accepts the name, salary as arguments


def __init__(self, name, salary):
# Initialize the values of instance attributes
self.name = name
self.salary = salary

# incrementing the employ count by 1


Employee.empCount += 1

# displayCount () which prints the total employ count


def displayCount(self):
print("Total Employee :",Employee.empCount)

# creating another function i.e, displayEmployee


def displayEmployee(self):
print ("Name : ", self.name, ", Salary: ", self.salary)

# creating child class with the name subEmployee inheriting properties


# from the parent Employee class
class subEmployee(Employee):
'Derived class for Base - Employee Class'

# printing Employee class with documentation using __doc__ class attribute


print("Employee class documentation using the __doc__ attribute:\n", Employee.__doc__)

# printing the name of the Employee class using __name__ class attribute
print("Name of the class using the __name__ attribute:\n", Employee.__name__)

# printing the module of the Employee class using __module__ class attribute
print("Module of the class using the __module__ attribute:\n", Employee.__module__)

# printing the dictionary containing the Employee class namespace


# using __dict__ class attribute
print("Dictionary containing the Employee class :\n", Employee.__dict__)

# printing all the base classes for the derived class using __bases__ class attribute
print("Base classes of subEmployee class:\n", subEmployee.__bases__)
e=Employee('john',24000)
print(e.name,e.salary)
e1=subEmployee(e.name,e.salary)
print(e1.name)
print(e1.salary)

Output
Employee class documentation using the __doc__ attribute:
Common base class for all employees
Name of the class using the __name__ attribute:
Employee
Module of the class using the __module__ attribute:
__main__
Dictionary containing the Employee class namespace:
{'__module__': '__main__', '__doc__': 'Common base class for all employees', 'empCount': 0, '__init__':
<function Employee.__init__ at 0x7f9532090050>, 'displayCount': <function Employee.displayCount
at 0x7f95320900e0>, 'displayEmployee': <function Employee.displayEmployee at 0x7f9532090170>,
'__dict__': <attribute '__dict__' of 'Employee' objects>, '__weakref__': <attribute '__weakref__' of
'Employee' objects>}
Base classes of subEmployee class: (<class '__main__.Employee'>,)
john 24000
john 24000
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.

Example 1 : Here is the simple example of destructor. By using del keyword we deleted all the
references of object ‘obj’, therefore destructor invoked automatically.

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.

Example 2: This example gives the explanation of above-mentioned note. Here, notice that the
destructor is called after the ‘Program End…’ printed.

class Employee:

# Initializing
def __init__(self):
print('Employee created')

# Calling destructor
def __del__(self):
print("Destructor called")

def Create_obj():
print('Making Object...')
obj = Employee()
print('function end...')
return obj

print('Calling Create_obj() function...')


obj = Create_obj()
print('Program End...')

Output
Calling Create_obj() function...
Making Object...
Employee created
function end...
Program End...
Destructor called

Example 3: Now, consider the following example :

class A:
def __init__(self, bb):
self.b = bb

class B:
def __init__(self):
self.a = A(self)
def __del__(self):
print("die")

def fun():
b = B()

fun()

Output
die

In this example when the function fun() is called, it creates an instance of class B which passes itself
to class A, which then sets a reference to class B and resulting in a circular reference.

Generally, Python’s garbage collector which is used to detect these types of cyclic references would
remove it but in this example the use of custom destructor marks this item as “uncollectable”.

Simply, it doesn’t know the order in which to destroy the objects, so it leaves them. Therefore, if
your instances are involved in circular references they will live in memory for as long as the
application run.
Example: Destruction in recursion
Here’s an example of how to use a destructor in a recursive function:

class RecursiveFunction:
def __init__(self, n):
self.n = n
print("Recursive function initialized with n =", n)

def run(self, n=None):


if n is None:
n = self.n
if n <= 0:
return
print("Running recursive function with n =", n)
self.run(n-1)

def __del__(self):
print("Recursive function object destroyed")

# Create an object of the class


obj = RecursiveFunction(5)

# Call the recursive function


obj.run()

# Destroy the object


del obj

Output
('Recursive function initialized with n =', 5)
('Running recursive function with n =', 5)
('Running recursive function with n =', 4)
('Running recursive function with n =', 3)
('Running recursive function with n =', 2)
('Running recursive function with n =', 1)
Recursive function object destroyed

In this example, we define a class RecursiveFunction with an __init__() method that takes in a
parameter n. This parameter is stored as an attribute of the object.
We also define a run() method that takes in an optional parameter n. If n is not provided, it defaults to
the value of self.n. The run() method runs a recursive function that prints a message to the console
and calls itself with n-1.
We define a destructor using the __del__() method, which simply prints a message to the console
indicating that the object has been destroyed.
We create an object of the class RecursiveFunction with n set to 5, and call the run() method. This
runs the recursive function, printing a message to the console for each call.
Finally, we destroy the object using the del statement. This triggers the destructor, which prints a
message to the console indicating that the object has been destroyed.
Note that in this example, the recursive function will continue running until n reaches 0. When n is 0,
the function will return and the object will be destroyed by the garbage collector. The destructor will
then be called automatically.

Advantages of using destructors in Python:


 Automatic cleanup: Destructors provide automatic cleanup of resources used by an object when
it is no longer needed. This can be especially useful in cases where resources are limited, or
where failure to clean up can lead to memory leaks or other issues.
 Consistent behavior: Destructors ensure that an object is properly cleaned up, regardless of how
it is used or when it is destroyed. This helps to ensure consistent behavior and can help to prevent
bugs and other issues.
 Easy to use: Destructors are easy to implement in Python, and can be defined using the __del__()
method.
 Supports object-oriented programming: Destructors are an important feature of object-oriented
programming, and can be used to enforce encapsulation and other principles of object-oriented
design.
 Helps with debugging: Destructors can be useful for debugging, as they can be used to trace the
lifecycle of an object and determine when it is being destroyed.

Overall, destructors are an important feature of Python and can help to ensure that objects are
properly cleaned up and resources are not wasted. They are easy to use and can be useful for
enforcing encapsulation and other principles of object-oriented design.
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. 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.

Consider a real-life example of encapsulation, in a company, there are different sections like the
accounts section, finance section, sales section etc. The finance section handles all the financial
transactions and keeps records of all the data related to finance. Similarly, the sales section handles
all the sales-related activities and keeps records of all the sales. Now there may arise a situation when
due to some reason an official from the finance section needs all the data about sales in a particular
month. In this case, he is not allowed to directly access the data of the sales section. He will first have
to contact some other officer in the sales section and then request him to give the particular data. This
is what encapsulation is. Here the data of the sales section and the employees that can manipulate
them are wrapped under a single name “sales section”. Using encapsulation also hides the data. In
this example, the data of the sections like sales, finance, or accounts are hidden from any other
section.
Protected members
Protected members (in C++ and JAVA) are those members of the class that cannot be accessed
outside the class but can be accessed from within the class and its subclasses. To accomplish this in
Python, just follow the convention by prefixing the name of the member by a single underscore
“_”.

Although the protected variable can be accessed out of the class as well as in the derived class
(modified too in derived class), it is customary(convention not a rule) to not access the protected out
the class body.
Note: The __init__ method is a constructor and runs as soon as an object of a class is instantiated.

# Python program to demonstrate protected members

# Creating a base class


class Base:
def __init__(self):

# Protected member
self._a = 2

# Creating a derived class


class Derived(Base):
def __init__(self):

# Calling constructor of Base class


Base.__init__(self)
print("Calling protected member of base class: ",self._a)

# Modify the protected variable:


self._a = 3
print("Calling modified protected member outside class: ",self._a)

obj1 = Derived()

obj2 = Base()

# Calling protected member can be accessed but should not be done due to convention
print("Accessing protected member of obj1: ", obj1._a)

# Accessing the protected variable outside


print("Accessing protected member of obj2: ", obj2._a)

Output:
Calling protected member of base class: 2
Calling modified protected member outside class: 3
Accessing protected member of obj1: 3
Accessing protected member of obj2: 2

Private members

Private members are similar to protected members, the difference is that the class members declared
private should neither be accessed outside the class nor by any base class. In Python, there is no
existence of Private instance variables that cannot be accessed except inside a class.
However, to define a private member prefix the member name with double underscore “__”.
Note: Python’s private and protected members can be accessed outside the class through python
name mangling.

Python program to demonstrate private members


# Creating a Base class
class Base:
def __init__(self):
self.a = "Good"
self.__c = "Good" #private

# Creating a derived class


class Derived(Base):
def __init__(self):

# Calling constructor of Base class


Base.__init__(self)
print("Calling private member of base class: ")
print(self.__c)
# Driver code
obj1 = Base()
print(obj1.a)

# Uncommenting print(obj1.c) will raise an AttributeError

# Uncommenting obj2 = Derived() will also raise an AttributeError as private member of base class
# is called inside derived class

Output:
Good
Traceback (most recent call last):
File "/home/f4905b43bfcf29567e360c709d3c52bd.py", line 25, in <module>
print(obj1.c)
AttributeError: 'Base' object has no attribute 'c'

Traceback (most recent call last):


File "/home/4d97a4efe3ea68e55f48f1e7c7ed39cf.py", line 27, in <module>
obj2 = Derived()
File "/home/4d97a4efe3ea68e55f48f1e7c7ed39cf.py", line 20, in __init__
print(self.__c)
AttributeError: 'Derived' object has no attribute '_Derived__c'
Data Hiding

 Data hiding is a concept which underlines the hiding of data or information from the user. It is
one of the key aspects of Object-Oriented programming strategies.
 It includes object details such as data members, internal work.
 Data hiding excludes full data entry to class members and defends object integrity by
preventing unintended changes.
 Data hiding also minimizes system complexity for increase robustness by limiting
interdependencies between software requirements.
 Data hiding is also known as information hiding. In class, if we declare the data members as
private so that no other class can access the data members, then it is a process of hiding data.

Data Hiding in Python:


 The Python document introduces Data Hiding as isolating the user from a part of program
implementation. Some objects in the module are kept internal, unseen, and unreachable to the
user. Modules in the program are easy to understand about how to use the application, but the
client cannot know how the application functions. Thus, data hiding imparts security, along
with discarding dependency.
 Data hiding in Python is the technique to defend access to specific users in the application.
 It is performed using the __ double underscore before done prefix. This makes the class
members non-public and isolated from the other classes.

Example:

class Solution:
__privateCounter = 0

def sum(self):
self.__privateCounter += 1
print(self.__privateCounter)

count = Solution()
count.sum()
count.sum()

# Here it will show error because it is unable to access private member


print(count.__privateCount)

Output:
Traceback (most recent call last):
File "/home/db01b918da68a3747044d44675be8872.py", line 11, in <module>
print(count.__privateCount)
AttributeError: 'Solution' object has no attribute '__privateCount'

To rectify the error, we can access the private member through the class name :
Class Solution:
__privateCounter = 0

def sum(self):
self.__privateCounter += 1
print(self.__privateCounter)

count = Solution()
count.sum()
count.sum()

# Here we have accessed the private data member through class name.
print(count._Solution__privateCounter)

Output:
1
2
2

Advantages of Data Hiding:


1. It helps to prevent damage or misuse of volatile data by hiding it from the public.
2. The class objects are disconnected from the irrelevant data.
3. It isolates objects as the basic concept of OOP.
4. It increases the security against hackers that are unable to access important data.

Disadvantages of Data Hiding:


1. It enables programmers to write lengthy code to hide important data from common clients.
2. The linkage between the visible and invisible data makes the objects work faster, but data hiding
prevents this linkage.

Inheritance
 Inheritance is defined as the mechanism of inheriting the properties of the base class to the
child class.
 One of the core concepts in object-oriented programming (OOP) languages is inheritance.
 It is a mechanism that allows you to create a hierarchy of classes that share a set of properties
and methods by deriving a class from another class.
 Inheritance is the capability of one class to derive or inherit the properties from another class.

Benefits of inheritance are:


Inheritance allows you to inherit the properties of a class, i.e., base class to another, i.e., derived
class. The benefits of Inheritance in Python are as follows:
 It represents real-world relationships.
 It provides the reusability of a code. We don’t have to write the same code again and again. Also,
it allows us to add more features to a class without modifying it.
 It is transitive in nature, which means that if class B inherits from another class A, then all the
subclasses of B would automatically inherit from class A.
 Inheritance offers a simple, understandable model structure.
 Less development and maintenance expenses.

Python Inheritance Syntax


The syntax of simple inheritance in Python is as follows:

class BaseClass:
{Body}
class DerivedClass(BaseClass):
{Body}

Creating a Parent Class


A parent class is a class whose properties are inherited by the child class. Let’s create a parent class
called Person which has a Display method to display the person’s information.

A Python program to demonstrate inheritance

class Person(object):

# Constructor
def __init__(self, name, id):
self.name = name
self.id = id

# To check if this person is an employee


def Display(self):
print(self.name, self.id)

# Driver code
emp = Person("Satyam", 102) # An Object of Person
emp.Display()

Output:
Satyam 102
Creating a Child Class
A child class is a class that drives the properties from its parent class. Here Emp is another class that
is going to inherit the properties of the Person class(base class).

class Emp(Person):

def Print(self):
print("Emp class called")

Emp_details = Emp("Mayank", 103)

# calling parent class function


Emp_details.Display()

# Calling child class function


Emp_details.Print()

Output:
Mayank 10
Emp class called

Example of Inheritance in Python


Let us see an example of simple Python inheritance in which a child class is inheriting the properties
of its parent class. In this example, ‘Person’ is the parent class, and ‘Employee’ is its child class.

# A Python program to demonstrate inheritance

class Person(object):

# Constructor
def __init__(self, name):
self.name = name

# To get name
def getName(self):
return self.name

# To check if this person is an employee


def isEmployee(self):
return False

# Inherited or Subclass (Note Person in bracket)


class Employee(Person):

# Here we return true


def isEmployee(self):
return True

# Driver code
emp = Person("Craig") # An Object of Person
print(emp.getName(), emp.isEmployee())

emp = Employee("Suman") # An Object of Employee


print(emp.getName(), emp.isEmployee())

Output:
Craig False
Suman True

What is an object class in Python?


Like the Java Object class, in Python (from version 3. x), the object is the root of all classes.
 In Python, “class Test(object)” and “class Test” are same.

Subclassing (Calling constructor of parent class)


A child class needs to identify which class is its parent class. This can be done by mentioning the
parent class name in the definition of the child class.

Example: class subclass_name (superclass_name)

In this example, ‘a’ is the instance created for the class Person. It invokes the __init__() of the
referred class. You can see ‘object’ written in the declaration of the class Person. In Python, every
class inherits from a built-in basic class called ‘object’. The constructor i.e. the ‘__init__’ function of
a class is invoked when we create an object variable or an instance of the class.
The variables defined within __init__() are called instance variables or objects. Hence, ‘name’ and
‘idnumber’ are the objects of the class Person. Similarly, ‘salary’ and ‘post’ are the objects of the
class Employee. Since the class Employee inherits from class Person, ‘name’ and ‘idnumber’ are also
the objects of class Employee.

Python code to demonstrate how parent constructors are called.

# parent class
class Person(object):

# __init__ is known as the constructor


def __init__(self, name, idnumber):
self.name = name
self.idnumber = idnumber

def display(self):
print(self.name)
print(self.idnumber)
# child class
class Employee(Person):
def __init__(self, name, idnumber, salary, post):
self.salary = salary
self.post = post

# invoking the __init__ of the parent class


Person.__init__(self, name, idnumber)

# creation of an object variable or an instance


a = Employee('Rahul', 886012, 200000, "Intern")

# calling a function of the class Person using its instance


a.display()

Output:
Rahul
886012

Python program to demonstrate error if we forget to invoke __init__() of the parent


If you forget to invoke the __init__() of the parent class then its instance variables would not be
available to the child class. The following code produces an error for the same reason.

Class A:
def __init__(self, n='Rahul'):
self.name = n

class B(A):
def __init__(self, roll):
self.roll = roll

object = B(23)
print(object.name)

Output :
Traceback (most recent call last):
File "/home/de4570cca20263ac2c4149f435dba22c.py", line 12, in
print (object.name)
AttributeError: 'B' object has no attribute 'name'

The super() Function


The super() function is a built-in function that returns the objects that represent the parent class. It
allows to access the parent class’s methods and attributes in the child class.

Example: super() function with simple Python inheritance


In this example, we created the object ‘obj’ of the child class. When we called the constructor of the
child class ‘Student’, it initialized the data members to the values passed during the object creation.
Then using the super() function, we invoked the constructor of the parent class.
# parent class
class Person():
def __init__(self, name, age):
self.name = name
self.age = age

def display(self):
print(self.name, self.age)

# child class
class Student(Person):
def __init__(self, name, age):
self.sName = name
self.sAge = age
# inheriting the properties of parent class
super().__init__("Rahul", age)

def displayInfo(self):
print(self.sName, self.sAge)

obj = Student("Mayank", 23)


obj.display()
obj.displayInfo()

Output:
Rahul 23
Mayank 23

Adding Properties
One of the features that inheritance provides is inheriting the properties of the parent class as well as
adding new properties of our own to the child class. Let us see this with an example:

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

def display(self):
print(self.name, self.age)

# child class
class Student(Person):
def __init__(self, name, age, dob):
self.sName = name
self.sAge = age
self.dob = dob
# inheriting the properties of parent class
super().__init__("Rahul", age)

def displayInfo(self):
print(self.sName, self.sAge, self.dob)

obj = Student("Mayank", 23, "16-03-2000")


obj.display()
obj.displayInfo()

Output:
Here we can see that we added a new property to the child class, i.e., date of birth (dob).
Rahul 23
Mayank 23 16-03-2000

Different types of Python Inheritance


There are 5 different types of inheritance in Python. They are as follows:
 Single inheritance: When a child class inherits from only one parent class, it is called single
inheritance. We saw an example above.
 Multiple inheritances: When a child class inherits from multiple parent classes, it is called
multiple inheritances.
Unlike Java, python shows multiple inheritances.

# Python example to show the working of multiple inheritance

class Base1(object):
def __init__(self):
self.str1 = "Good"
print("Base1")

class Base2(object):
def __init__(self):
self.str2 = "Morning"
print("Base2")
class Derived(Base1, Base2):
def __init__(self):

# Calling constructors of Base1


# and Base2 classes
Base1.__init__(self)
Base2.__init__(self)
print("Derived")

def printStrs(self):
print(self.str1, self.str2)

ob = Derived()
ob.printStrs()

Output:
Base1
Base2
Derived
Good Morning

 Multilevel inheritance: When we have a child and grandchild relationship. This means that a
child class will inherit from its parent class, which in turn is inheriting from its parent class.

class Base(object):

# Constructor
def __init__(self, name):
self.name = name

# To get name
def getName(self):
return self.name

# Inherited or Sub class (Note Person in bracket)


class Child(Base):

# Constructor
def __init__(self, name, age):
Base.__init__(self, name)
self.age = age

# To get name
def getAge(self):
return self.age
# Inherited or Sub class (Note Person in bracket)

class GrandChild(Child):

# Constructor
def __init__(self, name, age, address):
Child.__init__(self, name, age)
self.address = address

# To get address
def getAddress(self):
return self.address

# Driver code
g = GrandChild("Sandeep", 23, "Noida")
print(g.getName(), g.getAge(), g.getAddress())

Output:
Sandeep 23 Noida

 Hierarchical inheritance More than one derived class can be created from a single base.
 Hybrid inheritance: This form combines more than one form of inheritance. Basically, it is a
blend of more than one type of inheritance.

Private members of the parent class


We don’t always want the instance variables of the parent class to be inherited by the child class i.e.
we can make some of the instance variables of the parent class private, which won’t be available to
the child class.
In Python inheritance, we can make an instance variable private by adding double underscores before
its name. For example:

# Python program to demonstrate private members of the parent class

class C(object):
def __init__(self):
self.c = 21

# d is private instance variable


self.__d = 42

class D(C):
def __init__(self):
self.e = 84
C.__init__(self)
object1 = D()

# produces an error as d is private instance variable


print(object1.c)
print(object1.__d)

Output :
Here we can see that when we tried to print the variable ‘c’, its value 21 is printed on the console.
Whereas when we tried to print ‘d’, it generated the error. This is because the variable ‘d’ is made
private by using the underscores. It is not available to the child class ‘D’ and hence the error.
21
File "/home/993bb61c3e76cda5bb67bd9ea05956a1.py", line 16, in
print (object1.d)
AttributeError: type object 'D' has no attribute 'd'

Polymorphism
Polymorphism means having many forms. In programming, polymorphism means the same function
name (but different signatures) being used for different types. The key difference is the data types
and number of arguments used in function.

Example of inbuilt polymorphic functions:

Python program to demonstrate in-built polymorphic functions

# len() being used for a string


print(len("geeks"))

# len() being used for a list


print(len([10, 20, 30]))

Output
5
3

Examples of user-defined polymorphic functions:

# A simple Python function to demonstrate # Polymorphism

def add(x, y, z = 0):


return x + y+z

# Driver code
print(add(2, 3))
print(add(2, 3, 4))

Output
5
9

Polymorphism with class methods:


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

class India():
def capital(self):
print("New Delhi is the capital of India.")

def language(self):
print("Hindi is the most widely spoken language of India.")

def type(self):
print("India is a developing country.")

class USA():
def capital(self):
print("Washington, D.C. is the capital of USA.")

def language(self):
print("English is the primary language of USA.")

def type(self):
print("USA is a developed country.")

obj_ind = India()
obj_usa = USA()
for country in (obj_ind, obj_usa):
country.capital()
country.language()
country.type()

Output
New Delhi is the capital of India.
Hindi is the most widely spoken language of India.
India is a developing country.
Washington, D.C. is the capital of USA.
English is the primary language of USA.
USA is a developed country.

Polymorphism with Inheritance:

In Python, Polymorphism lets us define methods in the child class that have the same name as the
methods in the parent class. In inheritance, the child class inherits the methods from the parent class.
However, it is possible to modify a method in a child class that it has inherited from the parent class.
This is particularly useful in cases where the method inherited from the parent class doesn’t quite fit
the child class. In such cases, we re-implement the method in the child class. This process of re-
implementing a method in the child class is known as Method Overriding.

class Bird:
def intro(self):
print("There are many types of birds.")

def flight(self):
print("Most of the birds can fly but some cannot.")

class sparrow(Bird):
def flight(self):
print("Sparrows can fly.")

class ostrich(Bird):
def flight(self):
print("Ostriches cannot fly.")

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.

Polymorphism with a Function and objects:


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

def func(obj):
obj.capital()
obj.language()
obj.type()

obj_ind = India()
obj_usa = USA()

func(obj_ind)
func(obj_usa)

Code: Implementing Polymorphism with a Function

class India():
def capital(self):
print("New Delhi is the capital of India.")

def language(self):
print("Hindi is the most widely spoken language of India.")

def type(self):
print("India is a developing country.")

class USA():
def capital(self):
print("Washington, D.C. is the capital of USA.")

def language(self):
print("English is the primary language of USA.")

def type(self):
print("USA is a developed country.")

def func(obj):
obj.capital()
obj.language()
obj.type()

obj_ind = India()
obj_usa = USA()

func(obj_ind)
func(obj_usa)

Output
New Delhi is the capital of India.
Hindi is the most widely spoken language of India.
India is a developing country.
Washington, D.C. is the capital of USA.
English is the primary language of USA.
USA is a developed country.

polymorphism in Python using inheritance and method overriding:

class Animal:
def speak(self):
raise NotImplementedError("Subclass must implement this method")

class Dog(Animal):
def speak(self):
return "Woof!"

class Cat(Animal):
def speak(self):
return "Meow!"

# Create a list of Animal objects


animals = [Dog(), Cat()]

# Call the speak method on each object


for animal in animals:
print(animal.speak())

Output
Woof!
Meow!

You might also like