DS Python Chapter 1 Notes
DS Python Chapter 1 Notes
Advantages of POP:
1. Simplicity: Easy to learn and implement, especially for small programs.
2. Efficient for Small Tasks: Suitable for simple problems where the step-by-step
approach is sufficient.
3. Reusable Functions: Commonly used functions can be reused in different program
parts.
Disadvantages of POP:
1. Scalability Issues: Difficult to manage for large, complex programs.
2. Low Security: Global variables can be accessed and modified from anywhere,
increasing the risk of errors.
3. Reduced Modularity: Functions and data are separate, making the program less
modular and harder to debug or update.
Example of POP in C:
#include <stdio.h>
OOP Concepts
1. Class 8. Destructors
2. Objects 9. Message Passing
3. Encapsulation 10. Dynamic Binding
4. Abstraction 11. Association
5. Polymorphism 12. Modularity
6. Inheritance 13. Composition
7. Constructors
1. Encapsulation
• Definition: Encapsulation is bundling data (attributes) and methods (functions) into
a single unit, usually a class, and restricting access to some components.
• Purpose: It ensures controlled access to an object's data using access modifiers
like private, protected, and public.
•
• Example:
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private attribute
def get_balance(self):
return self.__balance
Here, __balance is hidden and only accessible through the get_balance() method.
2. Abstraction
• Definition: Abstraction focuses on exposing only the essential features of an object
while hiding implementation details.
• Purpose: Simplifies complexity by showing only relevant details and reduces code
dependencies.
• Example:
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius ** 2
The Shape class abstracts the concept of area computation, and the Circle class
implements the details.
3. Inheritance
• Definition: Inheritance allows one class (child/subclass) to inherit properties and
methods from another class (parent/superclass).
• Purpose: Promotes code reuse and establishes a hierarchical relationship between
classes.
• Example:
class Vehicle:
def start(self):
print("Vehicle started")
class Car(Vehicle):
def drive(self):
print("Car is driving")
my_car = Car()
my_car.start() # Inherited method
my_car.drive()
The Car class inherits the start() method from the Vehicle class.
4. Polymorphism
• Definition: Polymorphism means "many forms." It allows objects to be treated as
instances of their parent class, with the ability to override methods for specific
behavior.
• Purpose: Promotes flexibility and enables the same interface to handle different
data types or classes.
• Example:
class Bird:
def sound(self):
print("Bird makes a sound")
class Sparrow(Bird):
def sound(self):
print("Sparrow chirps")
class Eagle(Bird):
def sound(self):
print("Eagle screeches")
def make_sound(bird):
bird.sound()
Example:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
print(f"My name is {self.name} and I am {self.age} years
old.")
• Example:
class Engine:
def start(self):
print("Engine started")
class Car:
def __init__(self, engine):
self.engine = engine # Aggregation
def start_car(self):
self.engine.start()
engine = Engine()
my_car = Car(engine)
my_car.start_car()
7. Message Passing
• Definition: Objects communicate with each other by sending and receiving
information (messages) via method calls.
• Example:
class Calculator:
def add(self, x, y):
return x + y
calc = Calculator()
result = calc.add(5, 3) # Message sent to the object
print(result)
8. Dynamic Binding
• Definition: The process where a method call is resolved at runtime, enabling
polymorphism.
• Example:
class Animal:
def sound(self):
print("Some generic animal sound")
class Dog(Animal):
def sound(self):
print("Bark")
def make_sound(animal):
animal.sound() # Resolved at runtime
9. Modularity
• Definition: Breaking a program into smaller, self-contained units (classes and
objects) to promote reusability and maintainability.
• Example: Dividing a program into separate modules like User, Product, and Order.
@abstractmethod
def deposit(self, amount):
pass
@abstractmethod
def withdraw(self, amount):
pass
def calculate_interest(self):
return self._balance * self.__interest_rate
class CurrentAccount(Account):
def deposit(self, amount):
if amount > 0:
self._balance += amount
print(f"Deposited {amount} to Current Account.")
else:
print("Deposit amount must be positive.")
def display_all_accounts(self):
print(f"\nAccounts in {self.name}:")
for account in self.accounts:
self.account_summary(account)
# Polymorphism
@staticmethod
def account_summary(account):
print(f"Account Holder: {account._account_holder}")
print(f"Balance: {account.get_balance()}")
if isinstance(account, SavingsAccount):
print(f"Interest: {account.calculate_interest()}")
print("-----")
# Main Program: Demonstrates All OOP Features, Including Composition and
Modularity
if __name__ == "__main__":
# Modularity: Organized into multiple modules for clarity and reuse
# Creating Accounts
savings = SavingsAccount("Alice", balance=1000)
current = CurrentAccount("Bob", balance=500)
# Operations on Accounts
savings.deposit(500)
savings.withdraw(300)
current.deposit(200)
current.withdraw(700)
Real-Life Examples::
Encapsulation
1. Car Mechanism
• Encapsulation Concept: A car encapsulates complex mechanisms like the engine,
transmission, and braking systems.
• Real-Life Parallel: As a driver, you only interact with interfaces like the steering
wheel, pedals, and gear shifter without worrying about how the engine or brakes
work internally.
• In OOP Terms: The internal details of the car (engine mechanism) are private, and
you interact with it using public methods like accelerate(), brake(), and
changeGear().
2. Bank Account
• Encapsulation Concept: A bank account class may have private attributes like
account balance and account number.
• Real-Life Parallel: Customers can deposit, withdraw, or check the balance, but
they cannot directly manipulate the account balance.
• In OOP Terms: The attributes accountNumber and balance are private, and the
methods deposit(amount) and withdraw(amount) provide controlled access.
3. ATMs
• Encapsulation Concept: ATMs encapsulate functionality like cash withdrawal,
account inquiry, and password verification.
• Real-Life Parallel: You only interact with the ATM screen and keypad. You don't see
how the backend processes your card and PIN or connects to the bank's servers.
• In OOP Terms: The backend processes are private, while public methods are
provided for user interaction, such as enterPIN() or selectTransaction().
4. Smartphones
• Encapsulation Concept: Smartphones encapsulate functionalities like calling,
messaging, and internet browsing.
• Real-Life Parallel: As a user, you only use the apps (interfaces) and don't worry
about how the operating system or the hardware handles these operations.
• In OOP Terms: The hardware interactions are hidden (private), while apps provide
user-friendly public interfaces.
5. Online Shopping Systems
• Encapsulation Concept: E-commerce platforms encapsulate product catalogs,
payment processing, and order tracking.
• Real-Life Parallel: Customers can browse products, add items to the cart, and
make payments without knowing how inventory systems, payment gateways, or
logistics work.
• In OOP Terms: Attributes like product details and order status are private, and
public methods like addToCart(), checkout(), and trackOrder() allow controlled
interactions.
6. Healthcare Systems
• Encapsulation Concept: Patient data management systems encapsulate sensitive
information and provide restricted access.
• Real-Life Parallel: Doctors and staff can access patient records through authorized
systems, but not all hospital employees can see all details.
• In OOP Terms: Patient data is private, with controlled access provided through
secure methods like getPatientHistory().
7. Music Players
• Encapsulation Concept: A music player application encapsulates functionalities
like playing songs, adjusting volume, and creating playlists.
• Real-Life Parallel: Users interact with a simple interface, while the app handles
decoding and playing the audio files.
• In OOP Terms: Attributes like currentSong and volumeLevel are private, with public
methods like play(), pause(), and setVolume().
Abstraction
• A remote control for a television. The user interacts with buttons without needing to
understand the TV's internal workings.
• A smartphone interface where the user can use apps without knowing how the
software and hardware interact behind the scenes.
• A ride-sharing app where users book rides through an interface without
understanding the algorithm behind route optimization.
Inheritance
• A class Vehicle (base class) and subclasses like Car, Bike, and Truck that inherit
common properties (like wheels and fuel type) from Vehicle.
• A Person class that serves as a base class, with subclasses such as Employee,
Student, and Customer, which inherit common attributes like name and age while
adding specific properties.
• A Shape class as a base class with subclasses like Rectangle, Circle, and Triangle,
which have specific area calculation methods.
Polymorphism
• A function draw() that works for different shapes, such as Circles, Squares, and
Triangles, allowing the same method to operate on different object types.
• A payment() method that can process payments differently depending on whether
the payment type is a credit card, debit card, or digital wallet.
• A speak() method for different animal classes, such as Dog and Cat, where each class
implements the procedure to output the respective sound.
Constructor and Destructor
• A Game class where a constructor initializes the game state and a destructor
releases resources or saves the game when the player quits.
• An online shopping cart system where a constructor initializes a cart for users when
they log in, and a destructor clears the cart when the session ends.
• A ShoppingCart class that initializes with items when the user adds products and
clears the cart when the user completes the purchase.
Composition
• A Library that contains instances of Book and Member classes, where a library is
composed of books and members.
• A Computer class that includes CPU, RAM, and Storage components.
• A House consists of Room, Kitchen, and Bathroom classes, whereas a house
comprises multiple rooms.
1. Encapsulation
Encapsulation involves keeping data safe within a class by using private variables and
providing controlled access via methods.
class CoffeeMachine:
def __init__(self):
self.__water_level = 100 # Private variable (indicated by
__)
def make_coffee(self):
if self.__water_level >= 10:
self.__water_level -= 10
print("Coffee made!")
else:
print("Not enough water, please refill!")
machine = CoffeeMachine()
machine.make_coffee() # Output: Coffee made!
machine.add_water(20) # Adjust water level
2. Inheritance
Inheritance allows a class to inherit attributes and methods from another class. Below, Car
and Bike inherit common properties and methods from the Vehicle class.
class Vehicle:
def __init__(self, make, model):
self.make = make
self.model = model
def start_engine(self):
print("Engine started!")
class Car(Vehicle):
def __init__(self, make, model, doors):
super().__init__(make, model)
self.doors = doors
def car_info(self):
print(f"Car: {self.make} {self.model}, Doors: {self.doors}")
class Bike(Vehicle):
def bike_info(self):
print(f"Bike: {self.make} {self.model}")
3. Polymorphism
Polymorphism allows different classes to use the same method name but with different
implementations.
class TV:
def power(self):
print("TV is now ON")
class Radio:
def power(self):
print("Radio is now ON")
# Using polymorphism
for device in [TV(), Radio()]:
device.power() # Output: "TV is now ON" then "Radio is now ON"
4. Abstraction
Abstraction hides complex details and only shows the essential parts. Below, Shape is an
abstract class with a method area() that its subclasses must implement.
class Shape(ABC):
@abstractmethod
def area(self):
pass
class Circle(Shape):
def __init__(self, radius):
self.radius = radius
def area(self):
return 3.14 * self.radius * self.radius
class Rectangle(Shape):
def __init__(self, width, height):
self.width = width
self.height = height
def area(self):
return self.width * self.height
circle = Circle(5)
print("Circle area:", circle.area()) # Output: Circle area: 78.5
rectangle = Rectangle(4, 5)
print("Rectangle area:", rectangle.area()) # Output: Rectangle area: 20
def __del__(self):
print(f"{self.name} is deleted.")
# Creating an object of the Person class
person1 = Person("Alice")
person2 = Person("Bob")
person3 = Person("John")
person4 = Person("Myra")
person5 = Person("Myra")
1.3 Types of Variables, Types of Methods
(Instance & Static)
inside class
variables Outside class
vaiables / global
methods
student1 = School("John")
print(student1.student_name) # Output: John
print(School.school_name) # Output: XYZ School
1. Instance Variables
• Definition: Variables that are unique to each instance (object) of a class.
• Scope: Defined within methods (typically __init__) and prefixed with self to associate
them with an instance.
• Usage: Store data unique to each object.
• Access: Accessed and modified using the self keyword.
Example:
class Car:
def __init__(self, make, model):
self.make = make # Instance variable
self.model = model # Instance variable
# Creating objects
car1 = Car("Toyota", "Corolla")
car2 = Car("Honda", "Civic")
# Creating objects
car1 = Car("Toyota", "Corolla")
car2 = Car("Honda", "Civic")
print(car1.wheels) # Output: 4
print(car2.wheels) # Output: 4
# Modify the class variable
Car.wheels = 3
print(car1.wheels) # Output: 3
print(car2.wheels) # Output: 3
3. Local Variables
• Definition: Variables declared within a method and accessible only inside that
method.
• Scope: Exists only during the method's execution and is destroyed afterward.
• Usage: Used for temporary operations within a method.
Example:
python
Copy code
class Car:
def display_info(self):
message = "This is a local variable" # Local variable
print(message)
# Creating an object
car = Car()
car.display_info() # Output: This is a local variable
Summary Table:
1. Public Members
• Definition: Members (variables or methods) accessible from anywhere, both
inside and outside the class.
• Convention: Declared without any special prefix.
• Usage: For attributes and methods meant to be freely accessible.
Example:
class Example:
def __init__(self):
self.public_var = "I am Public" # Public variable
def public_method(self):
print("This is a public method.")
2. Protected Members
• Definition: Members intended to be accessible within the class and its subclasses.
• Convention: Prefixed with a single underscore (_).
• Enforcement: This is a convention, not strict enforcement. The member can still be
accessed from outside the class but is marked as "for internal use."
Example:
class Parent:
def __init__(self):
self._protected_var = "I am Protected" # Protected variable
def _protected_method(self):
print("This is a protected method.")
class Child(Parent):
def access_protected(self):
print(self._protected_var) # Accessing protected member in subclass
self._protected_method()
3. Private Members
• Definition: Members meant to be inaccessible directly from outside the class.
• Convention: Prefixed with a double underscore (__).
• Enforcement: Python uses name mangling to make it harder (not impossible) to
access private members from outside the class. The variable's actual name is
modified internally to include the class name.
Example:
class Example:
def __init__(self):
self.__private_var = "I am Private" # Private variable
def __private_method(self):
print("This is a private method.")
def access_private(self):
print(self.__private_var) # Accessing private member
self.__private_method()
Key Notes:
1. Name Mangling: Python changes private variables like __private_var to
_ClassName__private_var internally to make accidental access difficult.
2. Why the Flexibility? Python trusts the developer to follow conventions rather than
enforce strict rules.
3. Best Practices:
o Use public for general-purpose members.
o Use protected for members that subclasses need.
o Use private for sensitive data or methods you want to hide from external
access.
Types of methods in oops in python
@staticmethod
def subtract(x, y): # Static Method
return x - y
calc = Calculator()
print(calc.add(5, 3)) # Output: 8
print(Calculator.subtract(5, 3)) # Output: 2
Data Structures
Definition (D):
A Data Structure is a specific way of organizing, managing, and storing data in
a computer to be accessed and modified efficiently.
Definition:
A Data Structure is a tuple (D,F, A) where:
• D represents the data: the collection of values or elements to be stored and
manipulated.
• F represents the functions: the set of operations or methods performed on the data
(e.g., insert, delete, search, sort).
• A represents the axioms: the rules, properties, or constraints governing the structure
and behavior of the data.
This formal definition encapsulates the essence of data structures, emphasizing the
integration of data, functionality, and governing rules.
a) List:
• D: A sequence of elements stored in a linear order (e.g., [1, 2, 3, 4]).
• F:
o Access elements by index: list[index].
o Add elements: append(value), insert(index, value).
o Remove elements: pop(index), remove(value).
o Sort elements: sort(), reverse().
• A:
o Elements are stored in contiguous memory locations.
o Allows duplicates and mixed data types (e.g., [1, "hello", 3.14]).
o Maintains insertion order.
b) Tuple:
• D: An immutable sequence of elements (e.g., (1, 2, 3)).
• F:
o Access elements: tuple[index].
o Operations like slicing and iteration.
o Methods: count(value), index(value).
• A:
o Immutable (cannot change elements after creation).
o Supports heterogeneous data types.
c) Set:
• D: An unordered collection of unique elements (e.g., {1, 2, 3}).
• F:
o Add elements: add(value), update(values).
o Remove elements: discard(value), remove(value).
o Set operations: union(), intersection(), difference().
• A:
o No duplicate elements allowed.
o Unordered (no indexing).
d) Dictionary (dict):
• D: A collection of key-value pairs (e.g., {"key1": "value1", "key2": "value2"}).
• F:
o Add/update key-value pairs: dict[key] = value.
o Access value by key: dict[key].
o Remove pairs: pop(key), clear().
• A:
o Keys must be unique.
o Keys are immutable (e.g., strings, numbers, tuples).
f) File :
• D: Data
Stores raw or structured data (e.g., text, binary, JSON, CSV).
• F: Functions
Basic Operations: Create, Read, Write, Delete.
Advanced Operations: Append, Search, Lock, Compress.
• A: Axioms
Follows rules like format (text, binary, structured), access type (sequential or
random), and metadata constraints.
Key Insights:
Each data structure can be systematically analyzed using this tuple-based formalism. The
combination of DDD, FFF, and AAA highlights:
• The data stored.
• The functions available for manipulation.
• The axioms defining behavior and constraints.
Types Of Data
Structures in Python
Built-in Data User Defined
Structures Data Structures
list set Linear Data Non-Linear Data
tuple dict Structures Structures
b. Set
• Definition: Unordered, mutable, and does not allow duplicate elements.
• Example:
c. Tuple
• Definition: Ordered, immutable, and allows duplicate elements.
• Example:
d. Dictionary (dict)
• Definition: Mutable and stores key-value pairs.
• Example:
2. Linked Lists:
o Definition: Consists of nodes, where each node contains data and
a reference to the next node.
o Example:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
def display(self):
temp = self.head
while temp:
print(temp.data, end=" -> ")
temp = temp.next
print(None)
ll = LinkedList()
ll.append(10)
ll.append(20)
ll.append(30)
ll.display() # Output: 10 -> 20 -> 30 -> None
3. Queues:
o Definition: Follows FIFO (First-In-First-Out) principle.
o Example (using queue module):
class TreeNode:
def __init__(self, data):
self.data = data
self.children = []
root = TreeNode("Root")
child1 = TreeNode("Child 1")
child2 = TreeNode("Child 2")
root.add_child(child1)
root.add_child(child2)
root.display()
# Output:
# Root
# Child 1
# Child 2
2. Graph:
o Definition: Consists of nodes (vertices) connected by edges.
o Example:
graph = {
"A": ["B", "C"],
"B": ["A", "D", "E"],
"C": ["A", "F"],
"D": ["B"],
"E": ["B", "F"],
"F": ["C", "E"]
}
print(graph["A"])
my_list = [1, 2, 3]
my_list.append(4) # Adds 4 to the list
print(my_list) # Output: [1, 2, 3, 4]
my_tuple = (1, 2, 3)
print(my_tuple[0]) # Output: 1
my_set = {1, 2, 3}
my_set.add(4)
print(my_set) # Output: {1, 2, 3, 4}
-----------
| Key | Value |
-----------
| "name" | Alice |
| "age" | 25 |
-----------