Working With Classes
Before proceeding to build Packages in Python, we would like to discuss classes and how to write classes in our Python Program.
What is a class?
We all have heard about Object-Oriented Programming. Implementation of a Class in any programming language helps us to achieve the concept of the OOPs. A class consists of functions and data, which works as a blueprint, whenever we need to use the associated function and data in a given class, we create an object, which contains its local copy of function and data. So, we are able to access the function and data using object_name and dot operator.
Why Class is needed when the concept of function is already there?
There are many important concepts we realize through the implementation of classes such as Data Abstraction, Data Hiding, Inheritance, Polymorphism. You need to study Object-oriented programming if you want to go in detail.
One important fundamental: Whenever we import a module, which does not contain any class i.e. a simple python script with function and data. We import the function itself, so any other python program that wants to execute this module at the same time can produce unexpected results. Because two python programs will simultaneously share the resources of the given module.
But in the case of Class, we are free to do so, because class gives access to its function and data only through an object. And each object has its own local copy of function and data. Though some data can be shared between classes if we want to, which is further implementation of the OOPs concept.
How to write a Class in Python and access it?
Before proceeding further: just remember these points.
Writing a class:
Simply imagine you are writing a single function or group of functions using data.
PseudoCode:
class class_name(object)
def __init__(self, arg1, arg2, arg3, .., .., argN):
self.arg1 = arg1
self.arg2 = arg2
self.arg3 = arg3
self.argN = argN
def function_name_1(self):
print("I AM {} Color".format(self.arg1))
def function_name_2(self):
print("I AM {} Color".format(self.arg2))
def function_name_3(self):
print("I AM {} Color".format(self.arg3))
def function_name_N(self):
print("I AM {} Color".format(self.argN))
Please note: In the above code class, object, self, def, __init__ are reserved keyword and you need to remember these keywords for writing a class.
class_name, function_name_1, function_name_2, function_name_3, function_name_4 can be named anything (based on your choice) in compliance with permitted namespace in Python.
What is the use of self keyword: In the above code you can see self keyword repeating a lot of times. We are creating an object that contains a local copy of data variables, thus we use the self keyword where each object refers to its own data and associated function.
How to access it:
We need to create an object for a given class. When creating an object we can pass arguments to initialize data. Then we will access all the data and function using:
PseudoCode:
#Creating an Object & initializing with N arguments first_object = class_name(arg1, arg2, arg3, .., .., argN) #Calling function_name_1 first_object.function_name_1() #Calling function_name_2 first_object.function_name_2() #Calling function_name_3 first_object.function_name_3() #Calling function_name_N first_object.function_name_N()
Implementation of Calculator Python Program using Class:
Initializing Parameters while creating an object
Code:
class Calculator(object):
def __init__(self, a, b):
self.a = a
self.b = b
def add(self):
return self.a + self.b
def sub(self):
return self.a - self.b
def mul(self):
return self.a * self.b
def div(self):
return self.a / self.b
calc_obj = Calculator(99, 3)
print("Addition result is {}".format(calc_obj.add()))
print("Multiplication result is {}".format(calc_obj.mul()))
print("Division result is {}".format(calc_obj.div()))
print("Subtraction result is {}".format(calc_obj.sub()))
Output:
Addition result is 102 Multiplication result is 297 Division result is 33.0 Subtraction result is 96
Encapsulation, IS-A (Inheritance), HAS-A (Composition)
Encapsulation
Encapsulation: When we want to hide some data from other objects or we want to restrict access to some methods and variables. The concept of Encapsulation helps us to prevent direct manipulation or modification of data. Only, the function has access to private data. We hide attributes by making them private. In Python, we hide it by placing underscore as the prefix for the attribute, i.e single “ _ “ or double “ __“
In the below code, we have demonstrated that private class data can only be manipulated using class functions and not using an object.
Code:
class Color:
def __init__(self):
self.__color_name = "Green"
def Color_Type(self):
print("Color Type is : {}".format(self.__color_name))
def Color_Type_1(self, price):
self.__color_name = "Red"
c = Color()
c.Color_Type()
# changing the Color
c.__color_name = "Blue"
c.Color_Type()
# Setting color using Function
c.Color_Type_1(1000)
c.Color_Type()
Output:
Color Type is : Green Color Type is : Green Color Type is : Red
IS-A Relationship (Inheritance)
IS-A Relationship: Salmon is a Fish. It means Salmon inherit properties from a Fish. When a child class inherits the property of a parent class. We refer to it as a Parent-Child relationship.
There are three ways in which parent and child class can interact:
- Actions on the child imply an action on the parent: Implicit behavior
- Actions on the child override the action on the parent: Override Explicitly
- Actions on the child alter the action on the parent: Alter Before or After
Actions on the child imply an action on the parent: Implicit behavior
Implicit actions that happen when you define a function in the parent but not in
the child.
Code:
class Parent(object):
def function_parent(self):
print("I am parent")
class Child(Parent):
pass
a = Parent()
b = Child()
a.function_parent()
b.function_parent()
Output:
I am parent I am parent
Actions on the child override the action on the parent: Override Explicitly
Code:
class Parent(object):
def function(self):
print("I am parent")
class Child(Parent):
def function(self):
print("I am Child")
a = Parent()
b = Child()
a.function()
b.function()
Output:
I am parent I am Child
Actions on the child alter the action on the parent: Alter Before or After
The third way to use inheritance is a special case of overriding where you want to alter the behavior before or after the Parent class’s version runs. You first override the function just like in the last example, but now we will use a Python built-in function named super to get the Parent version to call. Here’s the example of doing that so you can make sense of this description:
Code:
class Parent(object):
def function(self):
print("I am parent")
class Child(Parent):
def function(self):
print("I am CHILD, BEFORE PARENT is called")
super(Child, self).function()
print("I am CHILD, AFTER PARENT is called")
a = Parent()
b = Child()
a.function()
b.function()
Output:
I am parent I am CHILD, BEFORE PARENT is called I am parent I am CHILD, AFTER PARENT is called
Demonstration of super for initialization and function calling from Child class
Using super() with __init__
Code:
class Cube(object):
def __init__(self, arg):
self.arg = arg
def CubeFunction(self):
self.CubeResult = self.arg * self.arg * self.arg
class CubeAdd(Cube):
def __init__(self, arg, arg1):
super().__init__(arg)
self.arg1 = arg1
def CubeAddFunction(self):
super(CubeAdd, self).CubeFunction()
self.CubeAddResult = self.CubeResult + self.arg1
b = CubeAdd(2,1)
b.CubeAddFunction()
print(b.CubeAddResult)
b = CubeAdd(3,1)
b.CubeAddFunction()
print(b.CubeAddResult)
b = CubeAdd(4,1)
b.CubeAddFunction()
print(b.CubeAddResult)
Output:
9 28 65
HAS-A Relationship (Composition)
HAS-A Relationship: A dog is a mammal and Parrot is a Bird. Dog inherits features from Mammal parent class and Parrot inherits features from Bird parent class. Sometimes, one class does not inherit all the features from a class. But both dog and mammal have a mouth. It means they have some common properties but do not inherit all. So, we achieve this functionality through composition, we directly call a function from another class, without inheriting all the featured from a class.
The following code demonstrate the use of Composition.
Code:
class Cube(object):
def __init__(self, arg):
self.arg = arg
def CubeFunction(self):
self.CubeResult = self.arg * self.arg * self.arg
class CubeAdd(Cube):
def __init__(self, arg, arg1):
self.arg1 = arg1
self.cube = Cube(arg)
def CubeAddFunction(self):
self.cube.CubeFunction()
self.CubeAddResult = self.cube.CubeResult + self.arg1
b = CubeAdd(2,1)
b.CubeAddFunction()
print(b.CubeAddResult)
b = CubeAdd(3,1)
b.CubeAddFunction()
print(b.CubeAddResult)
b = CubeAdd(4,1)
b.CubeAddFunction()
print(b.CubeAddResult)
Output:
9 28 65