Unit 1
Unit 1
1. Definition: Abstract Data type (ADT) is a type (or class) for objects whose behavior is defined by a set of values
and a set of operations. It mentions what operations are to be performed but not how these operations will be
implemented. It is called “abstract” because it gives an implementation-independent view.
2. Example: List ADT, Stack ADT, Queue ADT, set, tuple, dict
3. List in python :
4. Features of ADT: Data abstraction, information hiding, Data structure independence, encapsulation, Modularity,
Robustness
5. Advantage of ADT:
Encapsulation: ADTs provide a way to encapsulate data and operations into a single unit, making it easier
to manage and modify the data structure.
Abstraction: ADTs allow users to work with data structures without having to know the implementation
details, which can simplify programming and reduce errors.
Data Structure Independence: ADTs can be implemented using different data structures, which can make it
easier to adapt to changing needs and requirements.
Information Hiding: ADTs can protect the integrity of data by controlling access and preventing
unauthorized modifications.
Modularity: ADTs can be combined with other ADTs to form more complex data structures, which can
increase flexibility and modularity in programming.
List
Tuple
Dict
Set
Introduction to OOP
Class
1. Class creates a user-defined data structure, which holds its own data members and member functions, which can be
accessed and used by creating an instance of that class.
2. Classes are created by keyword class.
3. Attributes are always public and can be accessed using the dot (.) operator. Eg.: Myclass.Myattribute
4. Syntax:
class ClassName: # Class definition
# Statement
obj = ClassName() #Object Definition
print(obj.atrr)
Object
# A sample method
def fun(self):
print("I'm a", self.attr1)
print("I'm a", self.attr2)
# Object instantiation
Stud1 = Student()
Note: 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
Constructor
1. constructor contains a collection instructions that are executed at the time of Object creation. It runs as soon as an object
of a class is instantiated. The method is useful to do any initialization.
2. syntax:
def __init__(self):
# body of the constructor
3. Types : default constructor and parameterized constructor
default constructor parameterized constructor
class Student: class Student:
Destructor
1. Destructors are called when an object gets destroyed. In Python, destructors are not needed as much as in C++ because
Python has a garbage collector that handles memory management automatically.
2. syntax:
def __del__(self):
# body of destructor
3.Example:
class Employee: Output:
def __init__(self):
print('Employee created') Calling Create_obj() function...
Making Object...
def __del__(self): Employee created
print("Destructor called") function end...
Program End...
Destructor called
def Create_obj():
print('Making Object...')
obj = Employee()
print('function end...')
return obj
Stud1 = Student(12,'AB')
Stud1.fun()
Stud2=Student(13,'BC')
Stud2.fun()
Static variable
1. In Python, a static variable is a variable that is shared among all instances of a class, rather than being unique to each
instance. It is also sometimes referred to as a class variable, because it belongs to the class itself rather than any particular
instance of the class.
2. Static variables are defined inside the class definition, but outside of any method definitions.
3. Static variables are allocated memory once when the object for the class is created for the first time.
4. They are typically initialized with a value, just like an instance variable, but they can be accessed and modified through
the class itself, rather than through an instance.
5. Example
class Student:
TotalStudents=0
department='CSBS'
def __init__(self,a,b):
Student.TotalStudents+=1
self.ID = a
self.name = b
def fun(self):
print("I'm a student of", self.department)
print("My ID is", self.ID,'My name is',self.name)
Stud1 = Student(12,'AB')
Stud1.fun()
print('Total Students count =',Student.TotalStudents)
Stud2=Student(13,'BC')
Stud2.fun()
print('Total Students count =',Student.TotalStudents)
@classmethod
def fromBirthYear(cls, name, year):
return cls(name, 2023 - year)
@staticmethod
def isAdult(age):
return age > 18
Inheritance
1. One of the core concepts in object-oriented programming (OOP) languages is inheritance.
2. Inheritance is the capability of one class to derive or inherit the properties from another class.
3.Syntax:
Class BaseClass:
{Body}
Class DerivedClass(BaseClass):
{Body}
4. Example:
class Person(object):
def __init__(self, name, idnumber):
self.name = name
self.idnumber = idnumber
def display(self):
print(self.name)
print(self.idnumber)
class Employee(Person):
def __init__(self, name, idnumber, salary, post):
self.salary = salary
self.post = post
Person.__init__(self, name, idnumber)
6.Example:
Single- Person->Employee, Hierarchial – Shape->Cir, Rec, Squ , Multilevel- Vehicle->Four wheeler->Car
Hybrid- CollegeFaculty, StudentBook Multiple-> Mammal,WingedAnimal->Bat
7. Benefits:
It represents real-world relationships well.
It provides the reusability of a code.
Inheritance offers a simple, understandable model structure.Less development and maintenance expenses
Encapsulation
1. one of the fundamental concepts in object-oriented programming (OOP).
2. It is the concept of binding fields (object state) and methods (behavior) together as a single unit and preventing
unwanted access.
3. A class is an example of encapsulation as it encapsulates all the data that is member functions, variables, etc.
4. The goal of information hiding is to ensure that an object’s state is always valid by controlling access to attributes
that are hidden from the outside world.
5.
Private Protected Public
1. neither be accessed outside the class nor 1. cannot be accessed outside the class but can be In Python, there is
by any base class accessed from within the class and its subclasses. no existence of
2. In python, no existence of Private 2. It is convention but not a rule to not access the “Public” instance
instance variables protected out the class body. variables.
prefix the member name with double prefix the member name with single underscore
underscore “__”. “_”.
Example class Base:
class Base: def __init__(self):
def __init__(self): self.a = "InstVar"
self.a = "InstVar" self._c = "ProVar"
self.__c = "PriVar"
class Derived(Base):
class Derived(Base): def __init__(self):
def __init__(self): Base.__init__(self)
Base.__init__(self) print(self._c)
print(self.__c)
obj1 = Base()
obj1 = Base() print(obj1.a)
print(obj1.a)
obj2 = Derived()
# print(obj1.__c) # print(obj1._c)
# raise an AttributeError # Can be accessed but should not be
# obj2 = Derived() done due to convention
# raise an AttributeError
Polymorphism
1. polymorphism means having many forms. In programming, polymorphism means the same function name (but different
signatures) being used for different types. The key difference is the data types and number of arguments used in function.
2. Example Built-in Function –
print(len("geeks"))
print(len([10, 20, 30]))
User-defined polymorphic functions: def add(x, y, z = 0):
return x + y+z
print(add(2, 3))
print(add(2, 3, 4))
Polymorphism with class methods: class India():
def capital(self):
print("New Delhi”)
class USA():
def capital(self):
print("Washington, D.C.”)
obj_ind = India()
obj_usa = USA()
for country in (obj_ind, obj_usa):
country.capital()
Polymorphism with Inheritance and class Bird:
method overriding: 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()
Polymorphism with a Function and def func(obj):
objects: obj.capital()
obj_ind = India()
obj_usa = USA()
func(obj_ind)
func(obj_usa)
R = Triangle() R = Triangle()
R.noofsides() R.noofsides()
K = Quadrilateral() K = Quadrilateral()
K.noofsides() K.noofsides()
#p=Polygon() Can’t instantiate an abstract class #p=Polygon() Can’t instantiate an abstract class
Operator Overloading
1. Same built-in operator or function shows different behavior for objects of different classes, this is called Operator
Overloading.
2. Example : + is used to add two integers as well as join two strings and merge two lists.
Overloading binary + operator class A:
def __init__(self, a):
self.a = a
print(ob1 + ob2)
print(ob3 + ob4)
# Actual working when Binary Operator is used.
print(A.__add__(ob1 , ob2))
print(A.__add__(ob3,ob4))
#And can also be Understand as :
print(ob1.__add__(ob2))
print(ob3.__add__(ob4))
class complex:
def __init__(self, a, b):
self.a = a
self.b = b
Ob1 = complex(1, 2)
Ob2 = complex(2, 3)
Ob3 = Ob1 + Ob2
print(Ob3)
Overloading comparison operators class A:
def __init__(self, a):
self.a = a
def __gt__(self, other):
if(self.a>other.a):
return True
else:
return False
ob1 = A(2)
ob2 = A(3)
if(ob1>ob2):
print("ob1 is greater than ob2")
else:
print("ob2 is greater than ob1")
^=
Namespace
1. Whenever an identifier is assigned to a value, that definition is made with a specific scope.
2. Each distinct scope in Python is represented using an abstraction known as a namespace.
3. A namespace manages all identifiers that are currently defined in a given scope.
4. dir() - reports the names of the identifiers in a given namespace (i.e., the keys of the dictionary)
vars()- returns the full dictionary.
5. Types of namespaces : The built-in namespace encompasses the global namespace and the global namespace
encompasses the local namespace.
6. Ex. print(), id() are built-in namespaces. Depending on the version of Python, there are approximately 130–150
definitions that were deemed significant enough to be included in that built-in namespace.
7. same object name can be present in multiple namespaces as isolation between the same name is maintained by their
namespace.
some_method()
print(dir())
Modules
1. Attributes, functions, and classes that are organized as modules, that can be imported from within a program.
2. Example: math module mathematical functions (e.g., abs, min, max, round) , mathematical constants, pi and e
3. import statement loads definitions from a module into the current
namespace. Ex: from math import pi, sqrt
4. from math import * => danger is that some of the names defined in the module may conflict with names already in the
current namespace (or being imported from another module)
5. import math => individual definitions from the module can be accessed using a fully-qualified name, such as math.pi
or math.sqrt(2).
6. creating New module: if we were to put the definition of our count function into a file named utility.py, we could
import that function using the syntax, from utility import count.
1. palatte = warmtones
2. palette = list(warmtones)
3. Deepcopy?
# initializing list 1
li1 = [1, 2, [3, 5], 4]
print("li1 ID: ", id(li1), "Value: ", li1)
print("li1 ID: ", id(li1), "Value: ", li1)
# using copy for shallow copy
li2 = copy.copy(li1)
print("li2 ID: ", id(li2), "Value: ", li2)
# using deepcopy for deepcopy
li3 = copy.deepcopy(li1)
print("li3 ID: ", id(li3), "Value: ", li3)
li2[2][0]=10
print("li1 ID: ", id(li1), "Value: ", li1)
print("li2 ID: ", id(li2), "Value: ", li2)
print("li3 ID: ", id(li3), "Value: ", li3)
li3[2][0]=15
print("li1 ID: ", id(li1), "Value: ", li1)
print("li2 ID: ", id(li2), "Value: ", li2)
print("li3 ID: ", id(li3), "Value: ", li3)
Mutable, Immutable Objects
1. Whenever an object is instantiated, it is assigned a unique object id. The type of the object is defined at the runtime and
it can’t be changed afterwards. However, it’s state can be changed if it is a mutable object.
2^2^n > n! > 4^n > 2^n > n^2 > nlogn > log(n!) > n > 2^log n > (log n )^2 > sqrt(log n) > log log n >1
Recursion
1. It is a process in which a function calls itself directly or indirectly.
2. Advantages of using recursion
A complicated function can be split down into smaller sub-problems utilizing recursion.
Sequence creation is simpler through recursion than utilizing any nested iteration.
Recursive functions render the code look simple and effective.
3. Disadvantages of using recursion
A lot of memory and time is taken through recursive calls which makes it expensive for use.
Recursive functions are challenging to debug.
4. Syntax : def fn():
fn() //recursive call
5. Example:
def recursive_factorial(n):
if n == 1:
return n
else:
return n * recursive_factorial(n-1)