Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
103 views

Python Programming Inheritance

The document discusses access modifiers in Python using conventions rather than keywords. It explains that by default, all members are public. Protected members prefix a single underscore and can be accessed outside the class but is discouraged. Private members use double underscore and undergo name mangling to prevent direct access. The document also covers class methods, static methods, operator overloading, and code reuse techniques like composition and inheritance in Python.

Uploaded by

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

Python Programming Inheritance

The document discusses access modifiers in Python using conventions rather than keywords. It explains that by default, all members are public. Protected members prefix a single underscore and can be accessed outside the class but is discouraged. Private members use double underscore and undergo name mangling to prevent direct access. The document also covers class methods, static methods, operator overloading, and code reuse techniques like composition and inheritance in Python.

Uploaded by

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

Public, Protected and Private members:

- Access modifiers
- It is straight forward in c++ , Java - done using the keywords as public, protected,
private
- There is no direct way or keyword available in python for this purpose, yet its
possible using the convention methods.
- Public
- All member variables and methods are public by default.
- Can access class attributes and modify their values outside

class Employee:
def __init__(self, name, sal):
self.name = name
self.salary = sal

e1 = Employee("Aman", 10000)
print("Before Salary ", e1.salary)
e1.salary = 20000
print("After Salary ", e1.salary)

- Protected
- Standard meaning of protected is, class attributes are accessible within
class and its subclasses
- Prefixing single underscore ” _ ” to instance variables is a way of telling
that these are protected and not for accessing outside the class
- Although they can be modified outside as in the below examples shown
(Hence, the responsible programmer would refrain from accessing and
modifying instance variables prefixed with _ from outside its class)

class Employee:
def __init__(self, name, sal):
self._name = name
self._salary = sal

e1 = Employee("Aman", 10000)
print("Before Salary ", e1._salary)
e1._salary = 30000
print("After Salary ", e1._salary)

23
- Private
- Cant be accessed out side the class

class Employee:
def __init__(self, name, sal):
self.__name = name
self.__salary = sal

e1 = Employee("Aman", 10000)
print("Before Salary ", e1.__salary)

On executing above program…

AttributeError: 'Employee' object has no attribute '__salary'

- Python supports a technique called name mangling. Python performs name


mangling of private variables. Every member with double underscore will be
changed to object._ClassName__variable. If so required, it can still be accessed
from outside the class, but the practice should be refrained as shown below-

print("Before Salary ", e1._Employee__salary)

vars() and dir() built-in functions

- vars() returns dictionary of attributes and their values. It can be applied to class
and objects. When used with objects, returns class attributes and recursively
attributes of classes base class.
- dir() returns list of attributes. It can be applied to class and objects as well. When
used with objects, returns a list of objects attributes and objects class attributes
abd recursively attributes of its classes base class.

24
Python Intricacies:

- Below is an example of calling a class method from another class. Observe the
program and its output-

def display_all(): #global function


print("Inside Display All")

class Employee:
def __init__(self, n='',a=0, s=0): #instance method
self.__name = n
self.__age = a
self.__salary = s

def display_data(self): #instance method


print("Inside Employee display data")
print(self.__name, self.__age, self.__salary)
d1 = Department("CS",101)
d1.display_data() #calling a instance method of a class from
another class

print(Department.sample()) #calling a class method from another class


display_all() #calling global function

class Department:
def __init__(self, t, n): #instance method
self.__title = t
self.__id = n

def display_data(self): #instance method


print("Inside Department display data")
print(self.__title, self.__id)

def sample(): # class method


print("Inside Department sample")
# display_data() # will throw an error

e1 = Employee("Aman", 25, 30000)


e1.display_data()

25
Output of above code is below-

Inside Employee display data


Aman 25 30000
Inside Department display data
CS 101
Inside Department sample
None
Inside Display All

Static Method

- These methods are bound to the class not to the object


- Its is similar to class methods (not instance method) but unlike class
methods, it has nothing to do with class and deals with the parameters
passed to it.
- They can be called using staticmethodFunc()
- Used to group utility functions, to perform a task in isolation

class Mathematics:

def addNumbers(x, y):


return x + y

# create addNumbers static method


Mathematics.addNumbers = staticmethod(Mathematics.addNumbers)

print('The sum is:', Mathematics.addNumbers(5, 10))

- Another way of creating static methods is given below

class Dates:
def __init__(self, date):
self.date = date

def getDate(self):
return self.date

26
@staticmethod
def toDashDate(date):
return date.replace("/", "-")

date = Dates("15-12-2016")
dateFromDB = "15/12/2016"
dateWithDash = Dates.toDashDate(dateFromDB)

if(date.getDate() == dateWithDash):
print("Equal")
else:
print("Unequal")

- @staticmethod decorator, It is an expression that gets evaluated after our


function is defined.

Advantage of static methods-

- Less memory consumption, since in case of an instance method no of coppes of


the method will be equal to number of object.
- Utility functions
- Redability

Operator Overloading-

- Changing meaning of operators


- Depending upon the operands how the operator can behave is defined using
operator overloading techniques.
- So the meaning of operator for user defined data types can be defined.
- Special functions in python are used ( funcition which starts and ends with __ i.e.
a double underscore. ) Read more about the special functions here -
https://docs.python.org/3/reference/datamodel.html#special-method-names

Operators that can be overload in python are -

27
Operator Special function Operator Special function

+ __add__(self, other) < __lt__(self, other)

- __sub__(self, other) > __gt__(self, other)

* __mul__(self, other) <= __le__(self, other)

/ __truediv__(self, other) >= __ge__(self, other)

% __mod__(self, other) == __eq__(self, other)

** __pow__(self, other) != __ne__(self, other)

// __floordiv__(self, other)

Example of operator overloading is below-

class Point:
def __init__(self, x=0,y=0):
self.__x = x
self.__y = y

def display_points(self):
print("Points are: ", self.__x, self.__y)

# def __add__(self, other):


# return self.__x + other.__x, self.__y + other.__y

p1 = Point(2,3)
p1.display_points()

p2 = Point(5,5)
p2.display_points()

print("Combining points are: ", p1 + p2)

Output of the above code is here-

Points are: 2 3
Points are: 5 5

28
Traceback (most recent call last):
File "/Users/mspangtey/Downloads/DSEU 2022/Odd Sem Python/Python
Programs/operator_overloading.py", line 19, in <module>
print("Combining points are: ", p1 + p2)
TypeError: unsupported operand type(s) for +: 'Point' and 'Point'

On un commenting the __add__(self, other) method the output is-

Points are: 2 3
Points are: 5 5
Combining points are: (7, 8)

Code Reuse-

- Reusing the existing code


- Two mechanisms available to do so-
- Containership or Composition
- Used when two classes have a ‘has a’ relationship

Example:

class Department:
def __init__(self,n='',id=0):
self.__name = n
self.__id = id

def display_department(self):
print("Department id is {} and Name is {}".format(self.__id,
self.__name))

class Employee:
def __init__(self, n='', eid= 0):
self.__name = n
self.__eid = eid
self.d_obj = Department('ECE', 28)

def display_employee(self):
print("Employee Name is {}, id is {}".format(self.__name, self.__eid))

29
self.d_obj.display_department()
# print("Employee Name is {}, id is {}, Department is {} and
department id is {}".format(self.__name, self.__eid,
self.d_obj._Department__name, self.d_obj._Department__id))

e1 = Employee("Aman", 101)
e1.display_employee()

- Inheritance
- Used when two classes have a ‘like a’ relationship
- Derived Class can inherit features of Base Class
- Base class is also known as super-class or parent class
- Derived class is also known as subclass or child class
- Derived class object contains all base class data
- Derived class members can access base class members but vice
versa is not true.
- Convention for accessing variables
- var: access anywhere
- _var: access within class and derived class
- __var: access only within class
- Violating the conventions do not throw an error, for for good
practice it should follow the convention
- Accessing __var outside the class is not straight forward like
objectName.__var. This will give error. This is because variable
name gets mangled. So to access such variables the variable is
renames as _ClassName__var. Here ClassName is the class which
contains __var.

Example 1:

- Subclass Sparrow class inherits base class Bird.


- Derived class object can access all the methods of base class.
Example is speak method speak().
- The derived objects first find the speak method in Sparrow class,
once it doesnt find it here it searches the method in the base class.

class Bird:
def speak(self):

30
print("Inside Bird: Speak")

class Sparrow(Bird):
def sing(self):
print("Inside sparrow: sing")

s1 = Sparrow()
s1.sing()
s1.speak()

Output of above sample code:

Inside sparrow: sing


Inside Bird: Speak

Example 2:

- __init__ method is defined in both derived as well as in the base


class.
- In such cases, the derived class method overrides that in the base
class. Or __init__ of derived class has preference over base class
__init__.
- In order to call the base class init method, syntax is
super().__init__(3) or Polygon.__init__(self, 3) .
- super().__init__() should be used in case only one Class is
inherited. ClassName.__init__(self) should be used to call specific
inherited class.

class Polygon:
def __init__(self, n):
self.noOfSides = n
sides = [0 for i in range(self.noOfSides)]

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

def displaySides(self):
for i in range(self.noOfSides):
print("Side ",i+1, " is",self.sides[i])

31
class Triangle(Polygon):
def __init__(self):
super().__init__(3)
# Polygon.__init__(self, 3)

def findArea(self):
a, b, c = self.sides
s = (a+b+c)/2 # s is semiperimeter
area =(s*(s-a)*(s-b)*(s-c))**.5
print("Area of Triangle is %0.2f" %area)

t1 = Triangle()
t1.inputSides()
t1.displaySides()
t1.findArea()

Output of the above code is:

Enter side 1 : 20
Enter side 2 : 30
Enter side 3 : 40
Side 1 is 20.0
Side 2 is 30.0
Side 3 is 40.0
Area of Triangle is 290.47

32
Types of Inheritance-

Built-in methods to check inheritance:

- issubclass(sub, sup)
- Returns True is sub is derived from sub else returns False
- isinstance(obj, Class)
- Returns True if obj is an object or instance of Class else returns False

print(isinstance(t1, Triangle))
print(isinstance(t1, Polygon))
print(issubclass(Triangle, Polygon))
print(issubclass(Triangle, Polygon))

Output of above code is-

True
True
True

33
True

● In case of multiple Inheritance, the order in which methods are inherited is


defined by pythons method resolution technique. It can be viewed using
ClassName.__mro__
● There is something called diamond problem as shown in the below diagram. Der
class will have one copy of Base -> Derived1 and another copy of Base ->
Derived2. This will result in ambiguity.
● Python linearizes the order from left to right.

Example Below:
class Base:
def display(self):
print("Inside Base")

class Derived1(Base):
def display(Self):
print("Inside Derived 1")
super().display()

class Derived2(Base):
def display(self):
print("Inside Derived2")
super().display()

class Der(Derived1, Derived2):


def display(self):
print("Inside Der")
super().display()
d1 = Der()
d1.display()
print("")
print(Der.__mro__)

34
Abstract Class:

- Classes from which an object cant be created is called abstract class


- For this module named abc is imported and ABC class is inherited.
- A decorator @abstractmethod is used.
- If an abstract class contains only methods marked by the decorator
@abstractmethod, it is called an Interface

from abc import ABC, abstractmethod

class Student(ABC):

def __init__(self, first_name, lastname):


self.first_name = first_name
self.lastname = lastname

@property
def full_name(self):
return f"{self.first_name} {self.lastname}"

@abstractmethod
def getScore(self):
pass

35
class FullTime(Student):
def __init__(self,fn, ln, score):
super().__init__(fn, ln)
self.score = score

def getScore(self):
return self.score

class PartTime(Student):
def __init__(self,fn, ln, credit, hrs):
super().__init__(fn, ln)
self.credit = credit
self.hrs = hrs

def getScore(self):
return self.credit * self.hrs

# s1 = FullTime("Manjeet", "Pangtey", 19)

class ScoreBoard:
def __init__(self):
self.student_list = []

def add_student(self, stu):


self.student_list.append(stu)

def display_students(self):
for i in self.student_list:
print(f"{i.full_name} \t {i.getScore()}")

# st1 = Student("Ram", "Singh") # This line will throw error


sb = ScoreBoard()
sb.add_student(FullTime("Aman", "Kumar", 89))
sb.add_student(FullTime("Kiran", "Sharma", 90))
sb.add_student(FullTime("Sundaram", "Swami", 79))
sb.add_student(PartTime("Keshav", "Rao", 3, 8))
sb.add_student(PartTime("Madhav", "Bhatt", 5, 9))
sb.add_student(PartTime("Keshav", "Rao", 3, 8))

sb.display_students()

36

You might also like