Python Unit 4 Part 3
Python Unit 4 Part 3
Abstraction
Abstraction is one of the most powerful ideas in computer science. It separates what from the
how. Abstraction provides modularity.
Classes are the Python representation for “Abstract Data Types,” (ADT) a very useful notion in
any programming language. An ADT involves both data and operations on that data.
Example: Stack
A stack is a LIFO (last in, first out) list with the following opera:ons: Push, Pop, Create (Init),
Top, IsEmpty, Size. (Such a list is called a Signature or the Interface to the ADT.)
An abstract data type, or ADT, specifies a set of operations (or methods) and the semantics of the
operations (what they do), but it does not not specify the implementation of the operations. That’s
what makes it abstract.
1. It simplifies the task of specifying an algorithm if you can denote the operations you need without
having to think at the same time about how the operations are performed.
2. Since there are usually many ways to implement an ADT, it might be useful to write an algorithm
that can be used with any of the possible implementations.
3. Well-known ADTs, such as the Stack ADT in this chapter, are often implemented in standard
libraries so they can be written once and used by many programmers.
4. The operations on ADTs provide a common high-level language for specifying and talking about
algorithms.
When we talk about ADTs, we often distinguish the code that uses the ADT, called the client code,
from the code that implements the ADT, called the provider code.
Stack ADT
Stack() creates a new stack that is empty. It needs no parameters and returns an empty stack.
push(item) adds a new item to the top of the stack. It needs the item and returns nothing.
pop() removes the top item from the stack. It needs no parameters and returns
the item. The stack is modified.
top() returns the top item from the stack but does not remove it.
isEmpty() tests to see whether the stack is empty. It needs no parameters and returns a
boolean value.
size() returns the number of items on the stack. It needs no parameters and returns an
integer.
self.items = []
def IsEmpty(self):!
return self.items == []
def push(self,item):
self.items.append(item)
def pop(self):
return self.items.pop()
def top(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
Example: Queue
A Queue is a FIFO (first in, first out) list with the following opera:ons: Enqueue, Dequeue, Size,
Font.
Queue ADT
Queue() creates a new queue that is empty. It needs no parameters and returns an
empty queue.
Enqueue(item) adds a new item to the rear of the queue. It needs the item and returns
nothing.
Dequeue() removes the item from the front of the queue. It needs no parameters and
returns the item. The queue is modified.
Front() returns the front item from the queue but does not remove it. It needs no parameters. The
queue is not modified. isEmpty() tests to see whether the queue is empty. It needs no parameters
and returns a boolean value.
size() returns the number of items on the queue. It needs no parameters and returns an
integer.
def __init__(self):
self.items = []
def IsEmpty(self):
return self.items == []
def enqueue(self,item):
self.items.append(item)
def dequeue(self):
return self.items.pop(0)
def front(self):
return self.items[len(self.items)-1]
def size(self):
return len(self.items)
This code is called an implementation of the Stack ADT. In general, an implementation is a set of
methods that satisfy the syntactic and semantic requirements of an interface.
class Stack :
def __init__(self):
self.items = []
def pop(self):
return self.items.pop()
def is_empty(self):
return (self.items == [])
A Stack object contains an attribute named items that is a list of items in the stack. The
initialization method sets items to the empty list.
To push a new item onto the stack, push appends it onto items. To pop an item off the
stack, pop uses the homonymous ( same-named) list method to remove and return the last item on
the list.
Finally, to check if the stack is empty, is_empty compares items to the empty list.
An implementation like this, in which the methods consist of simple invocations of existing
methods, is called a veneer. In real life, veneer is a thin coating of good quality wood used in
furniture-making to hide lower quality wood underneath. Computer scientists use this metaphor to
describe a small piece of code that hides the details of an implementation and provides a simpler,
or more standard, interface.
The self
self represents the instance of the class. By using the “self” keyword we can access the attributes
and methods of the class in python. It binds the attributes with the given arguments.
The reason you need to use self. is because Python does not use the @ syntax to refer to instance
attributes. Python decided to do methods in a way that makes the instance to which the method
belongs be passed automatically, but not received automatically: the first parameter of methods
is the instance the method is called on
• 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.
• This is similar to this pointer in C++ and this reference in Java.
self is parameter in function and user can use another parameter name in place of it.But it is
advisable to use self because it increase the readability of code.
class this_is_class:
def show(in_place_of_self):
print("we have used another parameter name in place of self")
object = this_is_class()
object.show()
Output:
we have used another parameter name in place of self
# Python program to
# demonstrate instantiating
# a class
class Cat:
# A simple class
# attribute
attr1 = "mammal"
attr2 = "cat"
# A sample method
def fun(self):
# Driver code
# Object instantiation
Shadow = Cat()
print(Shadow.attr)
Shadow.fun()
Output:
mammal
She is a mammal
She is a cat
In the above example, an object is created which is basically a cat named Shadow. This class
only has two class attributes that tell us that Shadow is a cat and a mammal.
__init__ method
The __init__ method is similar to constructors in C++ and Java. Constructors are used to
initialize the object’s state. Like methods, a constructor also contains collection of statements(i.e.
instructions) that are executed at time of Object creation. 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:
self.name = name
# Sample Method
def say_hi(self):
p = Person('Nikhil')
p.say_hi()
Output:
Hello, my name is Nikhil
In the above example, a person name Nikhil is created. While creating a person, “Nikhil” is
passed as an argument, this argument will be passed to the __init__ method to initialize the
object. The keyword self represents the instance of a class and binds the attributes with the given
arguments. Similarly, many objects of Person class can be created by passing different names as
arguments.
Example:
# Sample Method
def say_hi(self):
print('Hello, my name is', self.name)
p1.say_hi()
p2.say_hi()
p3.say_hi()
Output:
Instance variables are for data unique to each instance and class variables are for attributes and
methods shared by all instances of the class. Instance variables are variables whose value is
assigned inside a constructor or method with self whereas class variables are variables whose
value is assigned in the class.
class Cat:
# Class Variable
animal = 'cat'
# Instance Variable
self.breed = breed
self.color = color
print('Shadow details:')
print('\nMisty details:')
Output:
Shadow details:
Shadow is a cog
Breed: Birman
Color: brown
Misty details:
Misty is a cat
Breed: Korat
Color: black
Accessing class variable using class name
cat
class Cat:
# Class Variable
animal = 'cat'
# Instance Variable
self.breed = breed
self.color = color
def getColor(self):
return self.color
# Driver Code
Shadow = Cat("Birman")
Shadow.setColor("brown")
print(Shadow.getColor())
Output:
brown
str() vs repr() in Python
str() and repr() both are used to get a string representation of object.
1. Example of str():
s = 'Hello, Geeks.'
print str(s)
print str(2.0/11.0)
Output:
Hello, Geeks.
0.181818181818
2. Example of repr():
s = 'Hello, Geeks.'
print repr(s)
print repr(2.0/11.0)
Output:
'Hello, Geeks.'
0.18181818181818182
From above output, we can see if we print string using repr() function then it prints with a pair of
quotes and if we calculate a value we get more precise value than str() function.
Following are differences:
• str() is used for creating output for end user while repr() is mainly used for debugging
and development. repr’s goal is to be unambiguous and str’s is to be readable. For
example, if we suspect a float has a small rounding error, repr will show us while str may
not.
• repr() compute the “official” string representation of an object (a representation that has all
information about the object) and str() is used to compute the “informal” string
representation of an object (a representation that is useful for printing the object).
• The print statement and str() built-in function uses __str__ to display the string
representation of the object while the repr() built-in function uses __repr__ to display the
object.
today = datetime.datetime.now()
print str(today)
print repr(today)
Output:
2016-02-22 19:32:04.078030
datetime.datetime(2016, 2, 22, 19, 32, 4, 78030)
str() displays today’s date in a way that the user can understand the date and time.
repr() prints “official” representation of a date-time object (means using the “official” string
representation we can reconstruct the object).
Binary Operators
Operator Method
+ object.__add__(self, other)
- object.__sub__(self, other)
* object.__mul__(self, other)
// object.__floordiv__(self, other)
/ object.__truediv__(self, other)
% object.__mod__(self, other)
** object.__pow__(self, other[, modulo])
<< object.__lshift__(self, other)
>> object.__rshift__(self, other)
& object.__and__(self, other)
^ object.__xor__(self, other)
| object.__or__(self, other)
Extended Assignments
Operator Method
+= object.__iadd__(self, other)
-= object.__isub__(self, other)
*= object.__imul__(self, other)
/= object.__idiv__(self, other)
//= object.__ifloordiv__(self, other)
%= object.__imod__(self, other)
**= object.__ipow__(self, other[, modulo])
<<= object.__ilshift__(self, other)
>>= object.__irshift__(self, other)
&= object.__iand__(self, other)
^= object.__ixor__(self, other)
|= object.__ior__(self, other)
Unary Operators
Operator Method
- object.__neg__(self)
+ object.__pos__(self)
abs() object.__abs__(self)
~ object.__invert__(self)
complex() object.__complex__(self)
int() object.__int__(self)
float() object.__float__(self)
Comparison Operators
Operator Method
< object.__lt__(self, other)
<= object.__le__(self, other)
== object.__eq__(self, other)
!= object.__ne__(self, other)
>= object.__ge__(self, other)
> object.__gt__(self, other)
Inheritance is the capability of one class to derive or inherit the properties from some another
class. The benefits of inheritance are:
1. It represents real-world relationships well.
2. It provides reusability of a code. We don’t have to write the same code again and again.
Also, it allows us to add more features to a class without modifying it.
3. It is transitive in nature, which means that if class B inherits from another class A, then all
the subclasses of B would automatically inherit from class A.
# parent class
class Bird:
def __init__(self):
print("Bird is ready")
def whoisThis(self):
print("Bird")
def swim(self):
print("Swim faster")
# child class
class Penguin(Bird):
def __init__(self):
# call super() function
super().__init__()
print("Penguin is ready")
def whoisThis(self):
print("Penguin")
def run(self):
print("Run faster")
peggy = Penguin()
peggy.whoisThis()
peggy.swim()
peggy.run()
Bird is ready
Penguin is ready
Penguin
Swim faster
Run faster
In the above program, we created two classes i.e. Bird (parent class) and Penguin (child class).
The child class inherits the functions of parent class. We can see this from swim() method.
Again, the child class modified the behavior of parent class. We can see this from whoisThis()
method. Furthermore, we extend the functions of parent class, by creating a new run() method.
Additionally, we use super() function before __init__() method. This is because we want to pull
the content of __init__() method from the parent class into the child class.
Encapsulation
Encapsulation is one of the fundamental concepts in object-oriented programming (OOP). It
describes the idea of wrapping data and the methods that work on data within one unit. This puts
restrictions on accessing variables and methods directly and can prevent the accidental
modification of data. To prevent accidental change, an object’s variable can only be changed by
an object’s method. Those type of variables are known as private varibale.
A class is an example of encapsulation as it encapsulates all the data that is member functions,
variables, etc.
Using OOP in Python, we can restrict access to methods and variables. This prevent data from
direct modification which is called encapsulation. In Python, we denote private attribute using
underscore as prefix i.e single “ _ “ or double “ __“.
Consider a real-life example of encapsulation, in a company, there are different sections like the
accounts section, finance section, sales section etc. The finance section handles all the financial
transactions and keeps records of all the data related to finance. Similarly, the sales section
handles all the sales-related activities and keeps records of all the sales. Now there may arise a
situation when for some reason an official from the finance section needs all the data about sales
in a particular month. In this case, he is not allowed to directly access the data of the sales
section. He will first have to contact some other officer in the sales section and then request him
to give the particular data. This is what encapsulation is. Here the data of the sales section and
the employees that can manipulate them are wrapped under a single name “sales section”. As
using encapsulation also hides the data. In this example, the data of any of the sections like sales,
finance or accounts are hidden from any other section.
def __init__(self):
self.__maxprice = 900
def sell(self):
print("Selling Price: {}".format(self.__maxprice))
c = Computer()
c.sell()
In the above program, we defined a class Computer. We use __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. To change the value, we used a setter
function i.e setMaxPrice() which takes price as parameter
Polymorphism
Polymorphism is an ability (in OOP) to use common interface for multiple form (data types).
Suppose, we need to color a shape, there are multiple shape option (rectangle, square, circle).
However we could use same method to color any shape. This concept is called Polymorphism.
def fly(self):
print("Parrot can fly")
def swim(self):
print("Parrot can't swim")
class Penguin:
def fly(self):
print("Penguin can't fly")
def swim(self):
print("Penguin can swim")
# common interface
def flying_test(bird):
bird.fly()
#instantiate objects
blu = Parrot()
peggy = Penguin()
In the above program, we defined two classes Parrot and Penguin. Each of them have common
method fly() method. However, their functions are different. To allow polymorphism, we
created common interface i.e flying_test() function that can take any object. Then, we passed the
objects blu and peggy in the flying_test() function, it ran effectively.