OOPs in Python
OOPS in python
Key terminologies
- Classes are logical collection of attributes and methods;
- Objects are instance of class.
- Actions performed are covered in Methods
- To access attribute of your class, use self parameter
- Process of creating your object is called object instantiation
Everything in python is an object.
eg - mylist = [1,2,3] --if we do type() on this, it returns o/t as 'class list'.
That means, list is a class in python, and above mylist is an object of class list.
So, if you call function, say, append, then it invokes method of class list.
mylist.append(4) -> [1,2,3,4]
Attributes
Attributes - an attribute is a property that further defines a class.
What is a class attribute?
- a class attribute are attributes that are shared across all instances of a class.
eg - no. of working hours. - they are created either as a part of the class or by using className.attributeName.
- so if edited or modified, then reflected for all object instances.
How to edit - to access them, use ClassName.ClassAttributeName and change value.
eg -
class Employee:
HoursOfWork = 40
x=Employee()
x.HoursOfWork --> output = 40
Now edit HoursOfWork -->
Employee.HoursOfWork = 30
x.HoursOfWork --> output = 30
We can also edit class attribute specific for an object instead for class as a whole.
eg -
class Employee:
HoursOfWork = 40
x=Employee()
x.HoursOfWork --> output = 40
Now edit HoursOfWork -->
x.HoursOfWork = 50
x.HoursOfWork --> output is 50 --> here we created another instance attribute called HoursOfWork(class attribute remains unchanged)
y=Employee()
y.HoursOfWork --> output = 40
What is Instance attribute?
Instance attribute are attributes that are specific to each instance of a class.
eg -
class Employee:
HoursOfWork = 40
x=Employee()
x.name = "ram" --> creates an instance attribute for object 'x'
x.name --> output = ram
y=Employee()
y.name --> give error coz no instance attribute associated with 'y'
They are created using objectName.attributeName
So, if edited or modified, then reflected only for that object instance.
How self parameter is handled?
The method call objectName.methodName() is interpreted as className.methodName(objectName), and this parameter is referred to as 'self' in method definition.
Methods
Attributes - an attribute is a property that further defines a class.
Method - a method is a function within a class which can access all the attributes of a class and perform a specific task.
What is Instance Method?
Methods that accept self parameter as default parameter.
They are used to modify instance attributes.
What is a static method?
Methods that do not modify instance attributes of a class are called Static Methods.
But they can be used to modify Class Attributes.
What is init() method?
init() method is the initializer in python.
It is called when an object is instantiated.
All the attributes of the class should be initialized in this method to make your object a fully initialized object.
It has the default parameter as the object are static methods.
self Variable
When you call any class method, then by default, first argument that is passed is class object name.
eg -
class Employee:
def employeeDetails()
pass
employeOne = Employee()
employeOne.employeeDetails() is same as employeeOne.emplpyeDetails(employeeOne)
By using self parameter, you are referring to attributes of that object, and the values persist until lifespan of program or until you manually delete the object. Eg -
class Employee:
def employeeDetails(self):
self.name = "kush"
print("name = ", self.name)
age = 30 --> since we didnt use "self", the value age is not available to other methods
print("age = ",age)
def printEmployeeDetails(self):
print("my name = ", self.name)
print("my age = ",age) --> here it will throw error since "age" is not available to other functions. Its scope is restricted to method employeeDetails.
Static Methods and Instance Methods
Instance Methods
- They are methods of our class that make use of self parameter and modify instance attributes of our class.
Static Methods-
- They are methods that do not take default self parameter.
- To differentiate between static and instance method, we use decorator @staticmethod
Decorators are functions that take another function as input and extend their functionality.
They are denoted by starting them with '@' symbol.
For example - @staticmethod is a decorator that takes the function as message, extends its functionality and ignores the binding of the object.
class Employee:
@staticmethod
def WelcomeMessage():
print("welcome to our organization")
init() method
- init() method is a special method in python which is used to initialize an object.
eg -
class Employee:
def setName(self):
self.name="ram"
def printName(self):
print(self.name)
x=employee()
x.printName() --> it gives error coz we havent initialized name.
- init() method is first method to be called in a class.
class Employee:
def __init__(self, name):
self.name = name
def printName(self):
print(self.name)
x=employee("ram")
x.printName() --> output is Ram
y=employee("shyam")
y.printName() --> output is shyam
Abstraction and Encapsulation
Abstraction is the process of hiding implementation details from the user.
Encapsulation is done to achieve abstraction.
You use method of class via abstraction.
Inheritance
- allows code re-usability.
Type 1 : single level inheritance
eg 1 -
dad = good baseball player.
son = good baseball player.
eg 2 -
class Apple:
manufacturer = "Apple Inc."
contactWebsite = "www.apple.com/contact"
def contactDetails(self):
print("To contact us, log on to ", self.contactWebsite )
class MacBook(Apple):
def __init__(self):
self.yearOfManufacture = 2017
def manufactureDetails(self):
print("This macbook was manufactured in year {} by {}"
.format(self.yearOfManufacture, self.manufacturer)")
mymacbook = MacBook()
# output = This macbook was manufactured in year 2017 by Apple Inc.
mymacbook.manufactureDetails
# To contact us, log on to www.apple.com/contact
mymacbook.contactDetails
Type2: Multiple Inheritances
eg 1 -
dad = good baseball player.
mom = good cook.
son = good baseball player and good cook
eg 2 -
class OperatingSystem:
multitasking = True
name = "Windows OS"
Class Apple:
website = "www.apple.com"
name = "Apple OS"
Class MacBook(OperatingSystem, Apple):
def __init__(self):
if self.multitasking is True:
# output = this is a multitasking system. Visit www.apple.com for more details.
print("this is a multi tasking system. Visit {} for more details".format(self.website))
#output = Window OS --why - coz we inherited OperatingSystem class first, and so variable present in both classes will be picked up from first class.
print("name = ".self.name)
Class NewMacBook(Apple, OperatingSystem):
def __init__(self):
if self.multitasking is True:
# output = this is a multitasking system. Visit www.apple.com for more details.
print("this is a multi tasking system. Visit {} for more details".format(seld.website))
# output = Apple OS --why - coz we inherited Apple class first, and so variable present in both classes will be picked up from first class.
print("name = ".self.name)
Type 3: multi level inheritance
eg 1-
grandfather = good baseball player
father = good soccer player
son = good baseball and soccer player
eg 2-
class MusicalInstruments:
numberOfMajorKeys = 12
class StringInstruments(MusicalInstruments):
typeOfWood = "ToneWood"
class Guitar(StringInstruments):
def __init__(self):
self.numberOfStrings = 6
print("this guitar consists of {} strings. It is made of {} and it can play {} keys"
.format(self.numberOfStrings, self.typeOfWood, self.numberOfMajorKeys))
guitar = Guitar()
output -
This guitar consists of 6 strings. It is made of ToneWood and can play 12 keys
Access Specifiers - public, private and protected
- public -
- your class, derived class, and further sub-classes derived from derived class --all can access values in main class.
- protected -
- only your class and derived class members can access data and methods of your class.
- private-
- only your class can access methods and attributes of your class.
To declare attribute or method as -
1. public - MemberName
2. Protected - _memberName --> single underscore.
3. Private - __MemberName --> double underscore.
class Car:
numberOfWheels = 4
_color = "Black"
__YearOfManufacture = 2018 #python saves it as _Car__yearOfManufacture i.e _ClassName__AttributeName
Class BMW(Car):
def __init__(self):
print("Protected attrubte color : ", self._color)
car=Car()
print("Public Attribute numberOfWheels: ", self.car.NumberOfWheels) --> output = Public Attribute NumberOfWheels: 4
bmw = BMW() --> output = Protected attribute color : Black
print("Private Attribute yearOfManufacture: ", car.__yearOfManufacture ) --> output = error
print("Private Attribute yearOfManufacture: ", car._Car__yearOfManufacture) --> output = Private attribute YearOfManufacture : 2018
PolyMorphism
It is characteristic of an entity to be able to be present in more than one form.
eg - "+" operator when applied to 2 numbers returns their sum, but whe applied to 2 strings, returns their concatenation.
Overriding and Super() method
Overriding = redefine base class method in your derived class.
super() function allows you to transfer control back to base class in a derived class.
It is used in case you want to access method in base class.
class Employee:
def setNumberOfWorkingHours(self):
self.NumberOfWorkingHours = 10
def displayNumberOfWorkingHours(self):
print(self.NumberOfWorkingHours)
employee = Employee()
employee.setNumberOfWorkingHours()
print("Number of working hours of emplyee = ", end = ' ')
employee.displayNumberOfWorkingHours()
output -
Number of working hours of Employee = 10
class Trainee(Employee):
def setNumberOfWorkingHours(self):
self.NumberOfWorkingHours = 20
def resetNumberOfWorkingHours(self):
super().setNumberOfWorkingHours()
trainee = Trainee()
trainee.setNumberOfWorkingHours()
print("Number of working hours of Trainee = ", end = ' ')
trainee.displayNumberOfWorkingHours()
trainee.resetNumberOfWorkingHours()
print("Number of working hours of Trainee after reset = ", end = ' ')
trainee.displayNumberOfWorkingHours()
output -
Number of working hours of Trainee = 20
Number of working hours of Trainee after reset = 10
Diamond Problem
Here we have 4 classes - A, B, C and D
- A is base class
- B anc C derived from A
- D is derived from B and C
Case 1: Method() will Not be overridden in Class B and Class C
class A:
def method(self):
print("this method belongs to class A")
pass
class B(A):
pass
class C(A):
pass
class D(B,C):
pass
d = D()
d.method()
output -->
This method belongs to class A
Case 2: Method() will be overridden in Class B but not in Class C
class A:
def method(self):
print("this method belongs to class A")
pass
class B(A):
def method(self):
print("this method belongs to class B")
class C(A):
pass
class D(B,C):
pass
d = D()
d.method()
output --> This method belongs to class B
Case 3: Method() will be overridden in Class C but not in Class B
class A:
def method(self):
print("this method belongs to class A")
class B(A):
pass
class C(A):
def method(self):
print("this method belongs to class C")
class D(B,C):
pass
d = D()
d.method()
output --> This method belongs to class C
Case 4: Method() will be overridden in both Class B and Class C
class A:
def method(self):
print("this method belongs to class A")
class B(A):
def method(self):
print("this method belongs to class B")
class C(A):
def method(self):
print("this method belongs to class C")
class D(B,C):
pass
d = D()
output -> This method belongs to class B
why this output ->
Method Resolution Order -- in case of derived class from multiple classes, method picked from 1st class to which it is obtained;
Had we defined class D as below, output would have been different.
class D(C,B):
pass
d = D()
d.method() --> output = This method belongs to class C
Operator Overloading
There are internal functions of python.
They are overridden in following way -
eg - overloading addition operator, the "+" operator when
- applied to 2 numbers returns their sum.
- applied to 2 strings, returns their concatenation.
class Square:
def __init__(self, side):
self.side = side
sq1 = Square(5)
sq2 = Square(10)
# output -> ERROR
# Why -> sq1 + sq2 = error coz '+' operator doesn't work on object of class square
print("the sum of sides of both the squares = ", sq1 + sq2)
So, we need a mechanism to add sides of square and return value.
That should allow us to add above 2 square objects as well.
Solution ->
overload special method add() in your class
class Square:
def __init__(self, side):
self.side = side
def __add__(square1, square2):
return( (4 * square1.side) + (4 * square2.side) )
sq1 = Square(5)
sq2 = Square(10)
# output -> 60
print("the sum of sides of both the squares = ", sq1 + sq2)
Abstract Base Class
An Abstract Base Class is a base class which doesn't have a definition of its own.
It has abstract methods which forces its implementation in derived classes
To declare a class as abstract base class, it needs to be declared as instance of ABCMeta class of python.
eg -
# we import ABCMeta and abstractmethod from abc module of python
from abc import ABCMeta, abstractmethod
class shape(metaclass = ABCMeta):
#decorator abstractmethod tells that given method is required to be defined in derived classes.
@abstractmethod
def area(self):
return 0
#here we say that method area() does not have a defn of its own; but it needs to be defined in derived classes.
class Square(shape):
side = 4
def area(self):
print("area of square", self.side * self.side)
class Rectangle(Shape):
width = 5
breadth = 10
def area(self):
print("area of rectangle " , self.width * self.length)
sq = Square()
rc = Rectangle()
sq.area()
rc.area()
Note - if no definition of method area() in class Square and even if we don't call sq.area() --> we get error
An abstract can only be inherited in derived classes, but cannot be instantiated.
If we try to create object of abstract class, we get error.
sh = Shape()
Summary
- polymorphism = ability of an entity to be able to exist in more than one form.
- overriding = form of polymorphism in which we redefine method of base class in a derived class.
- By doing this we change behaviour of a base class method.
- How -
class BaseClass:
def BaseClassMethod():
#define behaviour
class DerivedClass(BaseClass):
def baseClassMethod():
#redefine behaviour
-
Operator Overloading - defining a special method for an operator within your class to handle the operation between the objects of that class is called Operator Overloading.
-
Abstract Base Class - a base class which consists of abstract methods that should be implemented in its derived class is called abstract base class.
Syntax -
from abc import ABCMeta, abstractmethod
class baseClass(metaclass = ABCMeta):
@abstractmethod
def abstractMethod(self):
return