Object-Oriented Programming
- Object-oriented programming is a programming paradigm that provides a means of structuring programs so that properties and behaviors are bundled into individual objects.
- For instance, an object could represent a person with properties like a name, age, and address and behaviors such as walking, talking, breathing, and running. Or it could represent an email with properties like a recipient list, subject, and body and behaviors like adding attachments and sending.
- Object oriented programming using DRY(Do not Repeat Yourself) concept.
Classes
A class is a blueprint for the object.
We can think of class as a sketch of a parrot with labels. It contains all the details about the name, colors, size etc. Based on these descriptions, we can study about the parrot. Here, a parrot is an object.
The example for class of parrot can be :
class Parrot:
pass
Here, we use the class keyword to define an empty class Parrot. From class, we construct instances. An instance is a specific object created from a particular class.
Object
An object (instance) is an instantiation of a class. When class is defined, only the description for the object is defined. Therefore, no memory or storage is allocated.
The example for object of parrot class can be:
obj = Parrot()
Here, obj is an object of class Parrot
Just like every other Object Oriented Programming language Python supports classes.
- Classes are created by keyword class.
- Attributes are the variables that belong to class.
- Attributes are always public and can be accessed using dot (.) operator. Eg.: Myclass.Myattribute
# creates a class named MyClass
class MyClass:
# assign the values to the MyClass attributes
number = 0
name = "noname"
def Main():
# Creating an object of the MyClass.
# Here, 'me' is the object
me = MyClass()
# Accessing the attributes of MyClass
# using the dot(.) operator
me.number = 1337
me.name = "Harssh"
# str is an build-in function that
# creates an string
print(me.name + " " + str(me.number))
# telling python that there is main in the program.
if __name__=='__main__':
Main()
Output :
Harssh 1337
Methods
Method is a bunch of code that is intended to perform a particular task in your Python’s code.
- Function that belongs to a class is called an Method.
- All methods require ‘self’ parameter. If you have coded in other OOP language you can think of ‘self’ as the ‘this’ keyword which is used for the current object. It unhides the current instance variable.’self’ mostly work like ‘this’.
- ‘def’ keyword is used to create a new method.
# A Python program to demonstrate working of class
# methods
class Vector2D:
x = 0.0
y = 0.0
# Creating a method named Set
def Set(self, x, y):
self.x = x
self.y = y
def Main():
# vec is an object of class Vector2D
vec = Vector2D()
# Passing values to the function Set
# by using dot(.) operator.
vec.Set(5, 6)
print("X: " + str(vec.x) + ", Y: " + str(vec.y))
if __name__=='__main__':
Main()
Output :
X: 5, Y: 6
The self
- Class methods must have an extra first parameter in method definition. We do not give a value for this parameter when we call the method, Python provides it
- If we have a method which takes no arguments, then we still have to have one argument – the self. See fun() in above simple example.
- 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__ (Constructor) 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.
# A Sample class with init method
class Person:
# init method or constructor
def __init__(self, name):
self.name = name
# Sample Method
def say_hi(self):
print('Hello, my name is', self.name)
p = Person('Shwetanshu')
p.say_hi()
Output:
Hello, my name is Shwetanshu
Here, we define the __init__ method as taking a parameter name (along with the usual self).
Class and Instance Variables (Or attributes)
In Python, instance variables are variables whose value is assigned inside a constructor or method with self.
Class variables are variables whose value is assigned in class.
# Python program to show that the variables with a value
# assigned in class declaration, are class variables and
# variables inside methods and constructors are instance
# variables.
# Class for Computer Science Student
class CSStudent:
# Class Variable
stream = 'cse'
# The init method or constructor
def __init__(self, roll):
# Instance Variable
self.roll = roll
# Objects of CSStudent class
a = CSStudent(101)
b = CSStudent(102)
print(a.stream) # prints "cse"
print(b.stream) # prints "cse"
print(a.roll) # prints 101
# Class variables can be accessed using class
# name also
print(CSStudent.stream) # prints "cse"
We can define instance variables inside normal methods also.
# Python program to show that we can create
# instance variables inside methods
# Class for Computer Science Student
class CSStudent:
# Class Variable
stream = 'cse'
# The init method or constructor
def __init__(self, roll):
# Instance Variable
self.roll = roll
# Adds an instance variable
def setAddress(self, address):
self.address = address
# Retrieves instance variable
def getAddress(self):
return self.address
# Driver Code
a = CSStudent(101)
a.setAddress("Noida, UP")
print(a.getAddress())
Output :
Noida, UP
How to create an empty class?
We can create an empty class using pass statement in Python.
# An empty class
class Test:
pass
Encapsulation
Using OOP in Python, we can restrict access to methods and variables. This prevents data from direct modification which is called encapsulation. In Python, we denote private attributes using underscore as the prefix i.e single _ or double __.
Example 4: Data Encapsulation in Python
class Computer:
def __init__(self):
self.__maxprice = 900
def sell(self):
print("Selling Price: {}".format(self.__maxprice))
def setMaxPrice(self, price):
self.__maxprice = price
c = Computer()
c.sell()
# change the price
c.__maxprice = 1000
c.sell()
# using setter function
c.setMaxPrice(1000)
c.sell()
Output
Selling Price: 900
Selling Price: 900
Selling Price: 1000
In the above program, we defined a Computer class.
We used __init__() method to store the maximum selling price of Computer. We tried to modify the price. However, we can’t change it because Python treats the __maxprice as private attributes.
As shown, to change the value, we have to use a setter function i.e setMaxPrice() which takes price as a parameter.
Data Hiding and Object Printing
Data hiding
In Python, we use double underscore (Or __) before the attributes name and those attributes will not be directly visible outside.
class MyClass:
# Hidden member of MyClass
__hiddenVariable = 0
# A member method that changes
# __hiddenVariable
def add(self, increment):
self.__hiddenVariable += increment
print (self.__hiddenVariable)
# Driver code
myObject = MyClass()
myObject.add(2)
myObject.add(5)
# This line causes error
print (myObject.__hiddenVariable)
Output :
2
7
Traceback (most recent call last):
File "filename.py", line 13, in
print (myObject.__hiddenVariable)
AttributeError: MyClass instance has
no attribute '__hiddenVariable'
In the above program, we tried to access hidden variable outside the class using object and it threw an exception.
We can access the value of hidden attribute by a tricky syntax:
# A Python program to demonstrate that hidden
# members can be accessed outside a class
class MyClass:
# Hidden member of MyClass
__hiddenVariable = 10
# Driver code
myObject = MyClass()
print(myObject._MyClass__hiddenVariable)
Output :
10
Private methods are accessible outside their class, just not easily accessible. Nothing in Python is truly private; internally, the names of private methods and attributes are mangled and unmangled on the fly to make them seem inaccessible by their given names
Printing Objects
Printing objects gives us information about objects we are working with. In C++, we can do this by adding a friend ostream& operator << (ostream&, const Foobar&) method for the class. In Java, we use toString() method. In python this can be achieved by using __repr__ or __str__ methods.
class Test:
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return "Test a:%s b:%s" % (self.a, self.b)
def __str__(self):
return "From str method of Test: a is %s," \
"b is %s" % (self.a, self.b)
# Driver Code
t = Test(1234, 5678)
print(t) # This calls __str__()
print([t]) # This calls __repr__()
Output :
From str method of Test: a is 1234,b is 5678
[Test a:1234 b:5678]
Important Points about Printing:
- If no __str__ method is defined, print t (or print str(t)) uses __repr__
class Test:
def __init__(self, a, b):
self.a = a
self.b = b
def __repr__(self):
return "Test a:%s b:%s" % (self.a, self.b)
# Driver Code
t = Test(1234, 5678)
print(t)
Output :
Test a:1234 b:5678
- If no __repr__ method is defined then the default is used.
class Test:
def __init__(self, a, b):
self.a = a
self.b = b
# Driver Code
t = Test(1234, 5678)
print(t)
Output :
<__main__.Test instance at 0x7fa079da6710>
Inheritance
- Inheritance allows us to inherit all the methods and properties from another class
- Main advantages is usability of code
- By default each and every class in python inherit Object class
- One of the major advantages of Object Oriented Programming is re-use.
- Inheritance is one of the mechanisms to achieve the same.
- In inheritance, a class (usually called superclass) is inherited by another class (usually called subclass).
- The subclass adds some attributes to superclass.
# A Python program to demonstrate inheritance
# Base or Super class. Note object in bracket.
# (Generally, object is made ancestor of all classes)
# In Python 3.x "class Person" is
# equivalent to "class Person(object)"
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 employee
def isEmployee(self):
return False
# Inherited or Sub class (Note Person in bracket)
class Employee(Person):
# Here we return true
def isEmployee(self):
return True
# Driver code
emp = Person("Geek1") # An Object of Person
print(emp.getName(), emp.isEmployee())
emp = Employee("Geek2") # An Object of Employee
print(emp.getName(), emp.isEmployee())
Output:
('Geek1', False)
('Geek2', True)
How to check if a class is subclass of another?
Python provides a function issubclass() that directly tells us if a class is subclass of another class.
# Python example to check if a class is
# subclass of another
class Base(object):
pass # Empty Class
class Derived(Base):
pass # Empty Class
# Driver Code
print(issubclass(Derived, Base))
print(issubclass(Base, Derived))
d = Derived()
b = Base()
# b is not an instance of Derived
print(isinstance(b, Derived))
# But d is an instance of Base
print(isinstance(d, Base))
Output:
True
False
False
True
What is object class?
Like Java Object class, in Python (from version 3.x), object is root of all classes.
In Python 3.x, “class Test(object)” and “class Test” are same.
Does Python support Multiple Inheritance?
Unlike Java and like C++, Python supports multiple inheritance. We specify all parent classes as comma separated list in bracket.
# Python example to show working of multiple
# inheritance
class Base1(object):
def __init__(self):
self.str1 = "Geek1"
print "Base1"
class Base2(object):
def __init__(self):
self.str2 = "Geek2"
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
('Geek1', 'Geek2')
How to access parent members in a subclass?
Using Parent class name
# Python example to show that base
# class members can be accessed in
# derived class using base class name
class Base(object):
# Constructor
def __init__(self, x):
self.x = x
class Derived(Base):
# Constructor
def __init__(self, x, y):
Base.x = x
self.y = y
def printXY(self):
# print(self.x, self.y) will also work
print(Base.x, self.y)
# Driver Code
d = Derived(10, 20)
d.printXY()
Output:
(10, 20)
Using super()
We can also access parent class members using super
# Python example to show that base
# class members can be accessed in
# derived class using super()
class Base(object):
# Constructor
def __init__(self, x):
self.x = x
class Derived(Base):
# Constructor
def __init__(self, x, y):
''' In Python 3.x, "super().__init__(name)"
also works'''
super(Derived, self).__init__(x)
self.y = y
def printXY(self):
# Note that Base.x won't work here
# because super() is used in constructor
print(self.x, self.y)
# Driver Code
d = Derived(10, 20)
d.printXY()
Output:
(10, 20)
Types of inheritance Python
Types of Inheritance depends upon the number of child and parent classes involved. There are four types of inheritance in Python:
Single Inheritance:
Single inheritance enables a derived class to inherit properties from a single parent class, thus enabling code reusability and the addition of new features to existing code.
# Python program to demonstrate
# single inheritance
# Base class
class Parent:
def func1(self):
print("This function is in parent class.")
# Derived class
class Child(Parent):
def func2(self):
print("This function is in child class.")
# Driver's code
object = Child()
object.func1()
object.func2()
Output:
This function is in parent class.
This function is in child class.
Multiple Inheritance:
When a class can be derived from more than one base class this type of inheritance is called multiple inheritance. In multiple inheritance, all the features of the base classes are inherited into the derived class.
Example:
# Python program to demonstrate
# multiple inheritance
# Base class1
class Mother:
mothername = ""
def mother(self):
print(self.mothername)
# Base class2
class Father:
fathername = ""
def father(self):
print(self.fathername)
# Derived class
class Son(Mother, Father):
def parents(self):
print("Father :", self.fathername)
print("Mother :", self.mothername)
# Driver's code
s1 = Son()
s1.fathername = "RAM"
s1.mothername = "SITA"
s1.parents()
Output:
Father : RAM
Mother : SITA
Multilevel Inheritance
In multilevel inheritance, features of the base class and the derived class are further inherited into the new derived class. This is similar to a relationship representing a child and grandfather.
Example:
# Python program to demonstrate
# multilevel inheritance
# Base class
class Grandfather:
def __init__(self, grandfathername):
self.grandfathername = grandfathername
# Intermediate class
class Father(Grandfather):
def __init__(self, fathername, grandfathername):
self.fathername = fathername
# invoking constructor of Grandfather class
Grandfather.__init__(self, grandfathername)
# Derived class
class Son(Father):
def __init__(self,sonname, fathername, grandfathername):
self.sonname = sonname
# invoking constructor of Father class
Father.__init__(self, fathername, grandfathername)
def print_name(self):
print('Grandfather name :', self.grandfathername)
print("Father name :", self.fathername)
print("Son name :", self.sonname)
# Driver code
s1 = Son('Prince', 'Rampal', 'Lal mani')
print(s1.grandfathername)
s1.print_name()
Output:
Lal mani
Grandfather name : Lal mani
Father name : Rampal
Son name : Prince
Hierarchical Inheritance:
When more than one derived classes are created from a single base this type of inheritance is called hierarchical inheritance. In this program, we have a parent (base) class and two child (derived) classes.
Example:
# Python program to demonstrate
# Hierarchical inheritance
# Base class
class Parent:
def func1(self):
print("This function is in parent class.")
# Derived class1
class Child1(Parent):
def func2(self):
print("This function is in child 1.")
# Derivied class2
class Child2(Parent):
def func3(self):
print("This function is in child 2.")
# Driver's code
object1 = Child1()
object2 = Child2()
object1.func1()
object1.func2()
object2.func1()
object2.func3()
Output:
This function is in parent class.
This function is in child 1.
This function is in parent class.
This function is in child 2.
Hybrid Inheritance:
Inheritance consisting of multiple types of inheritance is called hybrid inheritance.
# Python program to demonstrate
# hybrid inheritance
class School:
def func1(self):
print("This function is in school.")
class Student1(School):
def func2(self):
print("This function is in student 1. ")
class Student2(School):
def func3(self):
print("This function is in student 2.")
class Student3(Student1, School):
def func4(self):
print("This function is in student 3.")
# Driver's code
object = Student3()
object.func1()
object.func2()
Output:
This function is in school.
This function is in student 1.
Class Method
- The @classmethod decorator, is a builtin function decorator that is an expression that gets evaluated after your function is defined. The result of that evaluation shadows your function definition.
- A class method receives the class as implicit first argument, just like an instance method receives the instance
Syntax:
class C(object):
@classmethod
def fun(cls, arg1, arg2, ...):
....
fun: function that needs to be converted into a class method
returns: a class method for function
- A class method is a method which is bound to the class and not the object of the class.
- They have the access to the state of the class as it takes a class parameter that points to the class and not the object instance.
- It can modify a class state that would apply across all the instances of the class. For example it can modify a class variable that will be applicable to all the instances.
Static Method
A static method does not receive an implicit first argument.
class C(object):
@staticmethod
def fun(arg1, arg2, ...):
...
returns: a static method for function fun.
- A static method is also a method which is bound to the class and not the object of the class.
- A static method can’t access or modify class state.
- It is present in a class because it makes sense for the method to be present in class.
Class method vs Static Method
- A class method takes cls as first parameter while a static method needs no specific parameters.
- A class method can access or modify class state while a static method can’t access or modify it.
- In general, static methods know nothing about class state. They are utility type methods that take some parameters and work upon those parameters. On the other hand class methods must have class as parameter.
- We use @classmethod decorator in python to create a class method and we use @staticmethod decorator to create a static method in python.
When to use what?
- We generally use class method to create factory methods. Factory methods return class object ( similar to a constructor ) for different use cases.
- We generally use static methods to create utility functions.
How to define a class method and a static method?
To define a class method in python, we use @classmethod decorator and to define a static method we use @staticmethod decorator.
Let us look at an example to understand the difference between both of them. Let us say we want to create a class Person. Now, python doesn’t support method overloading like C++ or Java so we use class methods to create factory methods. In the below example we use a class method to create a person object from birth year.
As explained above we use static methods to create utility functions. In the below example we use a static method to check if a person is adult or not.
Implementation
# Python program to demonstrate
# use of class method and static method.
from datetime import date
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# a class method to create a Person object by birth year.
@classmethod
def fromBirthYear(cls, name, year):
return cls(name, date.today().year - year)
# a static method to check if a Person is adult or not.
@staticmethod
def isAdult(age):
return age > 18
person1 = Person('mayank', 21)
person2 = Person.fromBirthYear('mayank', 1996)
print person1.age
print person2.age
# print the result
print Person.isAdult(22)
Output
21
21
True
Access Modifier
- Classical object-oriented languages, such as C++ and Java, control the access to class resources by public, private and protected keywords.
- Private members of a class are denied access from the environment outside the class. They can be handled only from within the class.
Similarly, a double underscore __ prefixed to a variable makes it private. It gives a strong suggestion not to touch it from outside the class. Any attempt to do so will result in an AttributeError:
- Public members (generally methods declared in a class) are accessible from outside the class. The object of the same class is required to invoke a public method.
- This arrangement of private instance variables and public methods ensures the principle of data encapsulation.
- Protected members of a class are accessible from within the class and are also available to its sub-classes.
Python’s convention to make an instance variable protected is to add a prefix _ (single underscore) to it. This effectively prevents it to be accessed, unless it is from within a sub-class.
- No other environment is permitted access to it. This enables specific resources of the parent class to be inherited by the child class.
- Python doesn’t have any mechanism that effectively restricts access to any instance variable or method. Python prescribes a convention of prefixing the name of the variable/method with single or double underscore to emulate the behaviour of protected and private access specifiers.
All members in a Python class are public by default. Any member can be accessed from outside the class environment.
Polymorphism in Python
What is Polymorphism : The word polymorphism means having many forms. In programming, polymorphism means same function name (but different signatures) being uses for different types.
Example of inbuilt polymorphic functions :
# Python program to demonstrate in-built poly-
# morphic functions
# len() being used for a string
print(len("geeks"))
# len() being used for a list
print(len([10, 20, 30]))
Output:
5
3
Polymorphism with class methods:
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, lets give the function something to do that uses the ‘obj’ object we passed to it. In this case lets 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.