0% found this document useful (0 votes)
39 views18 pages

Python Unit 4 Part 3

1. Abstraction separates what from how and provides modularity. Classes represent abstract data types which involve both data and operations on that data. 2. An abstract data type specifies operations and semantics but not implementation. This simplifies algorithms, allows multiple implementations, and provides common language for specifications. 3. A stack abstract data type includes operations like push, pop, peek, and isEmpty. A queue includes enqueue, dequeue, peek, and isEmpty. These can be implemented as classes using lists.

Uploaded by

Rahul Bhati
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
0% found this document useful (0 votes)
39 views18 pages

Python Unit 4 Part 3

1. Abstraction separates what from how and provides modularity. Classes represent abstract data types which involve both data and operations on that data. 2. An abstract data type specifies operations and semantics but not implementation. This simplifies algorithms, allows multiple implementations, and provides common language for specifications. 3. A stack abstract data type includes operations like push, pop, peek, and isEmpty. A queue includes enqueue, dequeue, peek, and isEmpty. These can be implemented as classes using lists.

Uploaded by

Rahul Bhati
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
Download as pdf or txt
Download as pdf or txt
You are on page 1/ 18

UNIT 4

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.)

Abstract data types


The data types you have seen so far are all concrete, in the sense that we have completely specified
how they are implemented. For example, the Card class represents a card using two integers. As
we discussed at the time, that is not the only way to represent a card; there are many alternative
implementations.

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.

Why is that useful?

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.

It needs no parameters. The stack is not modified.

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.

Stack ADT as a Class


class Stack:
def __init__(self):

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.

Queue ADT as a Class


Class Queue:

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)

Implementing stacks with Python lists


The list operations that Python provides are similar to the operations that define a stack. The
interface isn’t exactly what it is supposed to be, but we can write code to translate from the Stack
ADT to the built-in operations.

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.

Here is an implementation of the Stack ADT that uses a Python list:

class Stack :
def __init__(self):
self.items = []

def push(self, item):


self.items.append(item)

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.

Python Classes and Objects(Contd.)

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.

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) .

Self is a convention and not a real python keyword

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):

print("She is a", self.attr1)

print("She is a", self.attr2)

# Driver code
# Object instantiation

Shadow = Cat()

# Accessing class attributes


# and method through objects

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:

# 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('Nikhil')

p.say_hi()

Output:
Hello, my name is Nikhil

Understanding the code

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:

# 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)

# Creating different objects


p1 = Person('Nikhil')
p2 = Person('Abhinav')
p3 = Person('Anshul')

p1.say_hi()
p2.say_hi()
p3.say_hi()
Output:

Hello, my name is Nikhil


Hello, my name is Abhinav
Hello, my name is Anshul

Class and Instance Variables

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.

Defining instance variable using constructor


# 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 Cat:

# Class Variable

animal = 'cat'

# The init method or constructor

def __init__(self, breed, color):

# Instance Variable

self.breed = breed
self.color = color

# Objects of CSStudent class

Shadow = Cat("Birman", "brown")

Misty = Cat("Korat", "black")

print('Shadow details:')

print('Shadow is a', Shadow.animal)

print('Breed: ', Shadow.breed)

print('Color: ', Shadow.color)

print('\nMisty details:')

print('Misty is a', Misty.animal)

print('Breed: ', Misty breed)

print('Color: ', Misty.color)

# Class variables can be accessed using class


# name also

print("\nAccessing class variable using class name")


print(Cat.animal)

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

Defining instance variable using normal method


# Python program to show that we can create
# instance variables inside methods
# Class for Computer Science Student

class Cat:

# Class Variable

animal = 'cat'

# The init method or constructor

def __init__(self, breed):

# Instance Variable

self.breed = breed

# Adds an instance variable

def setColor(self, color):

self.color = color

# Retrieves instance variable

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.

Let understand this by an example:-


import datetime

today = datetime.datetime.now()

# Prints readable format for date-time object

print str(today)

# prints the official format of date-time object

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).

Arithmetic methods in Python

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)

Assignment Methods in Python

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)

Python Object Oriented Programming


Inheritance
Inheritance is a way of creating new class for using details of existing class without modifying it.
The newly formed class is a derived class (or child class). Similarly, the existing class is a base
class (or parent class).

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.

Example: Use of Inheritance in Python

# 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()

When we run this program, the output will be:

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.

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()
When we run this program, the output will be:

Selling Price: 900


Selling Price: 900
Selling Price: 1000

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.

Using Polymorphism in Python


class Parrot:

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()

# passing the object


flying_test(blu)
flying_test(peggy)

When we run above program, the output will be:

Parrot can fly


Penguin can't fly

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.

You might also like