Python Unit 2
Python Unit 2
Creating Classes, Instance Methods, Special Methods, Class Variables, Inheritance, Polymorphism, Type
Identification, Custom Exception Classes, Iterators, generators and decorators.
Python Classes/Objects
Create a Class
Example
class MyClass:
x=5
Create Object
Now we can use the class named MyClass to create objects:
Example
p1 = MyClass()
print(p1.x)
The examples above are classes and objects in their simplest form, and are not really useful in real life
applications.
To understand the meaning of classes we have to understand the built-in __init__() function.
All classes have a function called __init__(), which is always executed when the class is being initiated.
Use the __init__() function to assign values to object properties, or other operations that are necessary to do when
the object is being created:
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)
Note: The __init__() function is called automatically every time the class is being used to create a new object.
Object Methods
Objects can also contain methods. Methods in objects are functions that belong to the object.
Example
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def myfunc(self):
print("Hello my name is " + self.name)
p1 = Person("John", 36)
p1.myfunc()
The self Parameter
The self is used to represent the instance of the class. With this keyword, you can access the attributes and methods
of the class in python. It binds the attributes with the given arguments. The reason why we use self is that Python
does not use the ‘@’ syntax to refer to instance attributes.
self is a parameter in function and the user can use a different parameter name in place of it. Although it is
advisable to use self because it increases the readability of code.
Example
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()
Example
p1.age = 40
Example
Example
del p1
class definitions cannot be empty, but if you for some reason have a class definition with no content, put in
the pass statement to avoid getting an error.
Example
class Person:
pass
Special Methods
Special Methods are variety of instance methods that are reserved by Python, which affect an object’s high level
behaviour and its interactions with operators. These are known as special methods. __init__ is an example of a
special method; recall that it controls the process of creating instances of a class.
Similarly, we will see that __add__ controls the behaviour of an object when it is operated on by the + symbol,
for example. In general, the names of special methods take the form of __<name>__ , where the two underscores
precede and succeed the name. Accordingly, special methods can also be referred to as “dunder” (double-
underscore) methods. Learning to leverage special methods will enable us to design elegant and powerful classes
of objects.
The following special methods control how an object interacts with + , * , ** , and other mathematical operators.
A full listing of all the special methods used to emulate numeric types are
Constructor in Python
Constructor in Python is a special method which is used to initialize the members of a class during run-time when
an object is created.
In Python, we have some special built-in class methods which start with a double underscore (__) and they have a
special meaning in Python.
Every class must have a constructor, even if you don’t create a constructor explicitly it will create a default
constructor by itself.
Example:
class MyClass:
sum = 0
def printSum(self):
print(“Sum of a and b is: ”, self.sum)
Data hiding
Data hiding means making the data private so that it will not be accessible to the other class members. It can be
accessed only in the class where it is declared.
In python, if we want to hide the variable, then we need to write double underscore (__) before the variable name.
Example:
Class MyClass:
__num = 10
def add(self, a):
sum = self.__num + a
print(sum)
ob = MyClass()
ob.add(20)
print(ob.__num)
#The above statement gives an error because we are trying to access private variable outside the class
Output:
30
Traceback (most recent call last):
File “DataHiding.py”, line 10, in
print (ob.__num)
AttributeError: MyClass instance has
no attribute ‘__num
Inheritance
Example:
class Operations:
a = 10
b = 20
def add(self):
sum = self.a + self.b
print(“Sum of a and b is: “, sum)
class MyClass(Operations):
c = 50
d = 10
def sub(self):
sub = self.c – self.d
print(“Subtraction of c and d is: ”, sub)
ob = MyClass()
ob.add()
ob.sub()
Output:
Sum of a and b is: 30
Subtraction of c and d is: 40
In the above example, we are inheriting the properties of the ‘Operations’ class into the class ‘MyClass’.
Hence, we can access all the methods or statements present in the ‘Operations’ class by using the MyClass objects.
Which means the second class will inherit the properties of the first class and the third class will inherit the
properties of the second class. So the second class will act as both the Parent class as well as Child class.
Example:
class Addition:
a = 10
b = 20
def add(self):
sum = self.a + self.b
print(“Sum of a and b is: ”, sum)
class Subtraction(Addition):
def sub(self):
sub = self.b-self.a
print(“Subtraction of a and b is: ”, sub)
class Multiplication(Subtraction):
def mul(self):
multi = self.a * self.b
print(“Multiplication of a and b is: ”, multi)
ob = Multiplication ()
ob.add()
ob.sub()
ob.mul()
Output:
Sum of a and b is: 30
Subtraction of a and b is: 10
Multiplication of a and b is: 200
In the above example, class ‘Subtraction’ inherits the properties of class ‘Addition’ and class ‘Multiplication’ will
inherit the properties of class ‘Subtraction’. So class ‘Subtraction’ will act as both Base class and derived class.
class Subtraction():
c = 50
d = 10
def sub(self):
sub = self.c-self.d
print(“Subtraction of c and d is: ”, sub)
class Multiplication(Addition,Subtraction):
def mul(self):
multi = self.a * self.c
print(“Multiplication of a and c is: ”, multi)
ob = Multiplication ()
ob.add()
ob.sub()
ob.mul()
Output:
Sum of a and b is: 30
Subtraction of c and d is: 10
Multiplication of a and c is: 50
Python Inheritance
The inheritance is one of the important feature in object oriented programming, that gives you a way to reuse of
code.
Inheritance allows us to define a class that inherits all the methods and properties from another class.
Parent class is the class being inherited from, also called base class.
Child class is the class that inherits from another class, also called derived class.
Any class can be a parent class, so the syntax is the same as creating any other class:
Example
Create a class named Person, with firstname and lastname properties, and a printname method:
class Person:
def __init__(self, fname, lname):
self.firstname = fname
self.lastname = lname
def printname(self):
print(self.firstname, self.lastname)
#Use the Person class to create an object, and then execute the printname method:
x = Person("John", "Doe")
x.printname()
To create a class that inherits the functionality from another class, send the parent class as a parameter when
creating the child class:
Example
Create a class named Student, which will inherit the properties and methods from the Person class:
class Student(Person):
pass
Note: Use the pass keyword when you do not want to add any other properties or methods to the class.
Now the Student class has the same properties and methods as the Person class.
Example
Use the Student class to create an object, and then execute the printname method:
x = Student("Mike", "Olsen")
x.printname()
Polymorphism
Polymorphism means multiple forms. In python we can find the same operator or function taking multiple forms. It
also useful in creating different classes which will have class methods with same name. That helps in re using a lot
of code and decreases code complexity. Polymorphism is also linked to inheritance as we will see in some examples
below.
Polymorphism is used in Python programs when you have commonly named methods in two or more classes or
subclasses. It allows these methods to use objects of different types at different times. So, it provides flexibility,
using which a code can be extended and easily maintained over time.
Python allows us to define polymorphism in different ways. So, let us go ahead and see how it works.
Polymorphism in operators
The + operator can take two inputs and give us the result depending on what the inputs are. In the below examples
we can see how the integer inputs yield an integer and if one of the input is float then the result becomes a float. Also
for strings, they simply get concatenated. This happens automatically because of the way the + operator is created in
python.
Example
a = 23
b = 11
c = 9.5
s1 = "Hello"
s2 = "There!"
print(a + b)
print(type(a + b))
print(b + c)
print(type (b + c))
print(s1 + s2)
print(type(s1 + s2))
Running the above code gives us the following result −
Output
34
20.5
HelloThere!
Polymorphism in in-built functions
We can also see that different python functions can take inputs of different types and then process them differently.
When we supply a string value to len() it counts every letter in it. But if we five tuple or a dictionary as an input, it
processes them differently.
Example
str = 'Hi There !'
tup = ('Mon','Tue','wed','Thu','Fri')
lst = ['Jan','Feb','Mar','Apr']
dict = {'1D':'Line','2D':'Triangle','3D':'Sphere'}
print(len(str))
print(len(tup))
print(len(lst))
print(len(dict))
Running the above code gives us the following result −
Output
10
5
4
3
Polymorphism in user-defined methods
We can create methods with same name but wrapped under different class names. So we can keep calling the same
method with different class name pre-fixed to get different result. In the below example we have two classes,
rectangle and circle to get their perimeter and area using same methods.
Example
from math import pi
class Rectangle:
def __init__(self, length, breadth):
self.l = length
self.b = breadth
def perimeter(self):
return 2*(self.l + self.b)
def area(self):
return self.l * self.b
class Circle:
def __init__(self, radius):
self.r = radius
def perimeter(self):
return 2 * pi * self.r
def area(self):
return pi * self.r ** 2
type() method returns class type of the argument(object) passed as parameter. type() function is mostly used for
debugging purposes.
Two different types of arguments can be passed to type() function, single and three argument. If single argument
type(obj) is passed, it returns the type of given object. If three arguments type(name, bases, dict) is passed, it
returns a new type object.
Syntax :
type(object)
type(name, bases, dict)
Parameters :
name : name of class, which later corresponds to the __name__ attribute of the class.
bases : tuple of classes from which the current class derives. Later corresponds to the __bases__ attribute.
dict : a dictionary that holds the namespaces for the class. Later corresponds to the __dict__ attribute.
Returntype :
Example :
Output :
Iterators in Python
Iterator in python is an object that is used to iterate over iteratable objects like lists, tuples, dicts, and sets. The
iterator object is initialized using the iter() method. It uses the next() method for iteration.
print(next(myit))
print(next(myit))
print(next(myit))
Example:
for x in mytuple:
print(x)
The for loop actually creates an iterator object and executes the next() method
for each loop.
Create an Iterator
All classes have a function called __init__(), which allows you to do some
initializing when the object is being created.
The __iter__() method acts similar, you can do operations (initializing etc.), but
must always return the iterator object itself.
The __next__() method also allows you to do operations, and must return the
next item in the sequence.
Example
Create an iterator that returns numbers, starting with 1, and each sequence
will increase by one (returning 1,2,3,4,5 etc.):
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
StopIteration
The example above would continue forever if you had enough next()
statements, or if it was used in a for loop.
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
Generator-Function : A generator-function is defined like a normal function, but whenever it needs to generate a
value, it does so with the yield keyword rather than return. If the body of a def contains yield, the function
automatically becomes a generator function.
# A generator function that yields 1 for first time,
# 2 second time and 3 third time
def simpleGeneratorFun():
yield 1
yield 2
yield 3
Generator-Object : Generator functions return a generator object. Generator objects are used either by calling the
next method on the generator object or using the generator object in a “for in” loop (as shown in the above
program).
# A generator function
def simpleGeneratorFun():
yield 1
yield 2
yield 3
# x is a generator object
x = simpleGeneratorFun()
Decorators are a very powerful and useful tool in Python since it allows programmers to modify the behaviour of
function or class. Decorators allow us to wrap another function in order to extend the behaviour of the wrapped
function, without permanently modifying it. But before diving deep into decorators let us understand some
concepts that will come in handy in learning the decorators.
print(shout('Hello'))
yell = shout
print(yell('Hello'))
Multiple methods with the same name but with a different type of parameter or a different number of parameters is
called Method overloading
Example:
def product(a, b):
p = a*b
print(p)
#Gives you an error saying one more argument is missing as it updated to the second function
#product(2, 3)
product(2, 3, 5)
Output:
30
Conclusion
The class is a blueprint or template which contains all the details of an object, where the object is an instance of a
class.
• If we want to get the properties of another class into a class, then this can be achieved by
inheritance.
• Inheritance is of 3 types- Single Inheritance, Multilevel Inheritance, and Multiple Inheritance.
• Method overloading is not supported in Python.
• Method overriding is used to override the implementation of the same function which is defined
in another class.
• We can make the data attributes as private or hide them so that it will not be accessible outside
the class where it is defined.
Python Decorators
A decorator takes in a function, adds some functionality and returns it.
Decorators in Python
Python has an interesting feature called decorators to add functionality to an existing
code.
This is also called metaprogramming because a part of the program tries to modify
another part of the program at compile time.
Decorating Functions with Parameters
This function has two parameters, a and b . We know it will give an error if we pass
in b as 0.
>>> divide(2,5)
0.4
>>> divide(2,0)
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
Now let's make a decorator to check for this case that will cause the error.
def smart_divide(func):
def inner(a, b):
print("I am going to divide", a, "and", b)
if b == 0:
print("Whoops! cannot divide")
return
return func(a, b)
return inner
@smart_divide
def divide(a, b):
print(a/b)
This new implementation will return None if the error condition arises.
>>> divide(2,5)
I am going to divide 2 and 5
0.4
>>> divide(2,0)
I am going to divide 2 and 0
Whoops! cannot divide