Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
20 views

Updated Lecture Notes On Object Oriented Programming-1

Object oriented programming

Uploaded by

yinka
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views

Updated Lecture Notes On Object Oriented Programming-1

Object oriented programming

Uploaded by

yinka
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 19

Lecture Notes on Object-Oriented Programming (OOP)

Chapter 1: Introduction to Object-Oriented Programming


1.1 Definition of Object-Oriented Programming (OOP) Object-Oriented Programming (OOP)
is a programming paradigm that uses the concept of "objects" to model real-world entities.
Objects contain data (attributes) and methods (functions) that operate on that data. OOP focuses
on data abstraction, encapsulation, inheritance, and polymorphism to create modular, reusable,
and flexible software.
Object-Oriented Programming (OOP) is a popular programming paradigm centered around the
concept of "objects" that represent real-world entities. These objects consist of:
- Attributes (data): Variables that hold specific data related to the object, often referred to as
properties or fields.
- Methods (functions): Functions that define the behaviors and operations the object can perform.

OOP aims to provide a structured, modular approach to software development by combining data
and the operations on that data into objects. This paradigm encourages practices such as data
abstraction, encapsulation, inheritance, and polymorphism, which lead to better organized, more
reusable, and maintainable code.

By modeling entities as objects, developers can represent real-world elements in code (such as
people, cars, or bank accounts) and interact with these objects through their attributes and
methods.

Key Features of OOP:


- Abstraction: Simplifying complex systems by modeling them with objects and focusing on
essential features rather than intricate implementation details.
- Encapsulation: Bundling data (attributes) and behavior (methods) within objects and restricting
direct access to some of the object's components to safeguard its integrity.
- Inheritance: Allowing objects or classes to inherit properties and behaviors from other objects
or classes, facilitating code reuse.
- Polymorphism: Providing the ability for different objects to respond to the same method in
various ways, depending on their data type or class.

---

1.2 Key Concepts in OOP

# Class
A class is a blueprint or template for creating objects. It defines the structure and behavior that
the objects created from it will have. Classes typically include:
- Attributes: These are the characteristics or properties of the object (e.g., name, age, color).
- Methods: These are the functions that describe the behaviors of the object (e.g., walking,
driving, calculating).

Classes provide a way to bundle data and functionality together. For example, you could define a
`Car` class with attributes like `make`, `model`, and `year`, and methods like `start_engine()` or
`drive()`.

1
Example in Python:

```python
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year

def start_engine(self):
print(f"{self.year} {self.make} {self.model} engine starts.")
```

# Object
An object is an instance of a class. When a class is defined, no memory is allocated until an
object is created from that class. Objects are the fundamental building blocks of an OOP system
and represent actual entities in a program.

Objects are created from classes and can have:


- Attributes: The specific data that belongs to an object (e.g., a car's make and model).
- Methods: Actions or behaviors that an object can perform (e.g., starting a car engine).

Example of creating an object from the `Car` class:

```python
my_car = Car('Toyota', 'Corolla', 2020)
my_car.start_engine() # Outputs: 2020 Toyota Corolla engine starts.
```

Here, `my_car` is an object of the `Car` class with specific attributes (`make`, `model`, and
`year`) and the ability to execute the `start_engine` method.

# Attributes
Attributes, also called fields or properties, are the data variables that store information about an
object. Each object has its own set of attribute values. These attributes are defined in the class
but are specific to individual instances (objects) created from that class.

For example, in the `Car` class, the attributes include `make`, `model`, and `year`.

```python
# Accessing the attributes of an object
print(my_car.make) # Outputs: Toyota
print(my_car.year) # Outputs: 2020
```

2
# Methods
Methods are functions that belong to a class and describe the actions that an object can perform.
These methods are typically used to manipulate object attributes or perform operations related to
the object.

In the `Car` class, the `start_engine` method allows the car to perform the action of starting the
engine.

```python
# Calling a method on an object
my_car.start_engine() # Outputs: 2020 Toyota Corolla engine starts.
```

---

1.3 Importance of OOP

Object-Oriented Programming brings many advantages over traditional procedural


programming. These advantages are driven by OOP’s principles that emphasize organizing code
in a way that enhances scalability, maintainability, and reusability.

# Modularity
In OOP, code is divided into distinct classes and objects, making the software modular. Each
class is independent and can be developed, tested, and maintained separately from other classes.
This modularity helps in managing complex systems, as changes to one part of the system can be
made without affecting other parts.

For example, if a class responsible for handling database operations needs changes, those
changes can be isolated in that specific class without affecting other parts of the system.

# Reusability
Classes and objects can be reused across multiple projects, reducing duplication of code. Once a
class is written and tested, it can be used to create multiple objects or reused in other
applications. OOP promotes code reuse through:
- Inheritance, where new classes can inherit properties and behaviors from existing ones, and
- Composition, where classes can contain objects from other classes to extend functionality.

This reduces development time and ensures consistency across different parts of the program.

# Scalability
OOP systems are easier to scale. As projects grow in size and complexity, new features can be
added by creating new classes or extending existing ones through inheritance. This enables the
system to evolve while keeping the codebase clean and maintainable.

3
For example, if you want to add a new type of vehicle (e.g., `Truck`) to a system that already has
a `Car` class, you can create a `Truck` class that inherits from `Vehicle`, leveraging the existing
functionality and adding new behaviors specific to trucks.

# Maintainability
OOP's modular structure also makes it easier to maintain. Since the code is divided into
independent classes, bug fixes or changes can be made in one class without affecting the entire
system. This encapsulation ensures that internal implementations are hidden from the user, so as
long as the interface (public methods) remains the same, other parts of the system can interact
with the object as before.

In addition, the use of well-defined interfaces and clear separation of concerns (through
encapsulation) results in more understandable and maintainable codebases. For large projects,
this is a significant advantage because it reduces the likelihood of introducing bugs when making
changes.

Chapter 2: Core Principles of Object-Oriented Programming


Object-Oriented Programming (OOP) is built on four fundamental principles: Encapsulation,
Abstraction, Inheritance, and Polymorphism. These principles enable the creation of modular,
reusable, and scalable code. In this chapter, we will explore each of these principles in detail.
2.1 Encapsulation

Encapsulation is one of the core principles of OOP that involves bundling data (attributes) and
methods (functions) that operate on that data into a single unit, typically a class. Encapsulation
helps to protect the integrity of the data by restricting access and hiding implementation details.
It ensures that an object's internal state is shielded from unauthorized access or modification,
allowing control over how data is accessed and modified.

In encapsulation, data and methods are grouped together within a class, and access to this data is
controlled through access modifiers:
- Private: Data and methods are only accessible from within the class itself. This prevents outside
code from modifying the internal state of the object directly.
- Public: Data and methods are accessible from outside the class. Public members can be
accessed and modified by any code that creates an instance of the class.
- Protected: Data and methods are accessible within the class and its subclasses (classes that
inherit from the base class). Protected members are not accessible outside the class hierarchy.

# Benefits of Encapsulation:
- Data Security: By restricting direct access to an object's internal state, encapsulation helps
protect sensitive data.
- Modularity: Each class can be developed, tested, and maintained separately.
- Maintainability: Internal changes to the class do not affect other parts of the program as long as
the public interface remains unchanged.

# Example of Encapsulation in Python:


```python

4
class Employee:
def __init__(self, name, salary):
self.name = name # Public attribute
self.__salary = salary # Private attribute (indicated by double underscores)

# Public method to access the private attribute


def get_salary(self):
return self.__salary

# Public method to set the private attribute


def set_salary(self, salary):
if salary > 0:
self.__salary = salary
else:
print("Invalid salary amount")

# Creating an instance of the Employee class


employee = Employee('John Doe', 50000)

# Accessing the public attribute


print(employee.name) # Output: John Doe

# Trying to access the private attribute directly (will raise an AttributeError)


# print(employee.__salary)

# Using public methods to access and modify the private attribute


print(employee.get_salary()) # Output: 50000
employee.set_salary(60000)
print(employee.get_salary()) # Output: 60000
```

---

2.2 Abstraction

Abstraction refers to the concept of hiding the internal complexity of a system and exposing only
the necessary parts to the user. It allows developers to focus on *what* an object does rather than
*how* it does it. By providing a simplified interface, abstraction reduces complexity and
enhances usability.

In OOP, abstraction is often achieved through abstract classes and interfaces. An abstract class is
a class that cannot be instantiated on its own and may contain abstract methods (methods without
implementation). Subclasses that inherit from abstract classes must provide implementations for
these methods.

# Benefits of Abstraction:

5
- Simplicity: Users interact with an object using a simplified interface, hiding complex
implementation details.
- Code Reusability: Abstraction promotes reusable code by allowing different implementations
of the same interface.
- Maintainability: By separating implementation details from the user, abstraction makes it easier
to modify and extend the system without impacting other components.

# Example of Abstraction in Python:


```python
from abc import ABC, abstractmethod

# Abstract class
class Animal(ABC):

# Abstract method
@abstractmethod
def sound(self):
pass

# Subclass inheriting the abstract class


class Dog(Animal):
def sound(self):
return "Bark"

class Cat(Animal):
def sound(self):
return "Meow"

# Creating instances of the subclasses


dog = Dog()
cat = Cat()

print(dog.sound()) # Output: Bark


print(cat.sound()) # Output: Meow
```
In this example, the `Animal` class defines an abstract method `sound()`, which must be
implemented by any subclass (like `Dog` and `Cat`), allowing different behaviors while keeping
the interface simple.

---

2.3 Inheritance

Inheritance is a mechanism in OOP that allows a class (called a *subclass* or *derived class*) to
inherit properties and methods from another class (called a *superclass* or *base class*). This

6
promotes code reuse and establishes a natural hierarchy between classes. Inheritance allows
subclasses to:
- Inherit attributes and methods from the superclass.
- Extend or override the functionality of the superclass by adding new methods or modifying
existing ones.

# Types of Inheritance:
- Single Inheritance: A subclass inherits from one superclass.
- Multiple Inheritance: A subclass inherits from multiple superclasses (supported in some
languages like Python).
- Multilevel Inheritance: A subclass inherits from a superclass, which in turn inherits from
another superclass, forming a chain of inheritance.

# Benefits of Inheritance:
- Code Reusability: Inheritance allows for reuse of existing code, reducing duplication.
- Maintainability: Modifications made to the superclass automatically propagate to subclasses.
- Extensibility: Subclasses can extend the functionality of the superclass by adding new features.

# Example of Inheritance in Python:


```python
# Superclass
class Vehicle:
def __init__(self, make, model):
self.make = make
self.model = model

def start(self):
print(f"{self.make} {self.model} is starting.")

# Subclass inheriting from Vehicle


class Car(Vehicle):
def start(self):
super().start() # Call the superclass method
print("The car is now ready to drive.")

# Creating an instance of the subclass


my_car = Car("Toyota", "Corolla")
my_car.start()
```
Output:
```
Toyota Corolla is starting.
The car is now ready to drive.
```
In this example, the `Car` class inherits from the `Vehicle` class and extends its behavior by
overriding the `start()` method.

7
---

2.4 Polymorphism

Polymorphism refers to the ability of methods to take on different forms depending on the object
that invokes them. It enables the same method to behave differently depending on the context.
Polymorphism promotes flexibility and dynamic method invocation in OOP.

There are two main types of polymorphism:


- Method Overloading: Defining multiple methods with the same name but different parameter
signatures. Not supported in all languages, but many support this via default or optional
parameters.
- Method Overriding: A subclass provides a specific implementation of a method that is already
defined in its superclass. This allows the subclass to modify or enhance the behavior of the
inherited method.

# Benefits of Polymorphism:
- Flexibility: Polymorphism allows methods to behave differently depending on the object
invoking them.
- Code Reusability: The same method can be reused across different classes with different
behaviors, reducing code duplication.
- Extensibility: New classes can override inherited methods to implement new behavior without
modifying the original class.

# Example of Polymorphism in Python (Method Overriding):


```python
class Animal:
def sound(self):
print("Animal makes a sound")

class Dog(Animal):
def sound(self):
print("Dog barks")

class Cat(Animal):
def sound(self):
print("Cat meows")

# Using polymorphism
animals = [Dog(), Cat()]

for animal in animals:


animal.sound()
```
Output:

8
```
Dog barks
Cat meows
```
In this example, the `sound()` method behaves differently depending on the object (`Dog` or
`Cat`) that invokes it. This is an example of method overriding in action.

---

Chapter 3: Classes and Objects


In Object-Oriented Programming (OOP), classes and objects are fundamental concepts that form
the foundation of how code is structured and organized. A class is a blueprint or template for
creating objects, and an object is an instance of a class. This chapter will cover how to define a
class, create objects, and work with attributes and methods.

3.1 Defining a Class


A class in OOP is a blueprint for creating objects. It defines a set of attributes (data) and methods
(functions) that the objects created from the class will have. In simple terms, a class provides the
structure for what objects of that type should look like and how they should behave.
The syntax for defining a class typically involves specifying its attributes and methods. In
Python, for example, you define a class using the class keyword:
Example: Defining a Class in Python
python
Copy code
class Car:
def __init__(self, make, model, year):
self.make = make # Instance Attribute
self.model = model # Instance Attribute
self.year = year # Instance Attribute

def start_engine(self): # Instance Method


print(f"The {self.year} {self.make} {self.model} engine starts.")
In this example:
 Car is the name of the class.
 The __init__ method is a special constructor method that gets called when a new object is
created. It initializes the object’s attributes: make, model, and year.
 start_engine is a method defined inside the class that describes the behavior of the object
(starting the engine in this case).
Components of a Class:
1. Attributes: Variables that store the state of the object. In the example above, make,
model, and year are attributes.
2. Methods: Functions that define the behavior of the object. The start_engine() method is
an example of an instance method.

3.2 Creating Objects

9
An object is an instance of a class. Objects are created by calling the class constructor. In
Python, you create an object by calling the class name as if it were a function, passing in any
arguments required by the __init__ method.
Example: Creating Objects in Python
python
Copy code
# Creating an object of the Car class
my_car = Car('Toyota', 'Corolla', 2020)

# Calling a method on the object


my_car.start_engine() # Output: The 2020 Toyota Corolla engine starts.
In this example, my_car is an object of the class Car, with the attributes make='Toyota',
model='Corolla', and year=2020. The object then calls the start_engine() method, which outputs
a message based on the attributes of that particular object.
Key Points:
 Each object has its own unique data (attributes), which can vary between instances.
 Objects interact with the methods defined in the class to perform actions.
3.3 Object Attributes and Methods
Objects can have two types of attributes and methods:
1. Instance Attributes and Methods: These are tied to a specific object.
2. Class Attributes and Methods: These are shared across all instances of a class.
3.3.1 Instance Attributes
Instance attributes are attributes that belong to individual objects. Each object created from a
class has its own set of instance attributes. These attributes are initialized by the class constructor
(__init__ method) and can be different for each object.
Example:
python
Copy code
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year

my_car = Car('Toyota', 'Corolla', 2020)


your_car = Car('Honda', 'Civic', 2018)

print(my_car.make) # Output: Toyota


print(your_car.make) # Output: Honda
In this example, my_car and your_car are two different objects with their own instance attributes
(make, model, and year). Changing the attributes of one object does not affect the other.

3.3.2 Class Attributes


Class attributes are attributes that are shared across all instances of a class. These attributes are
defined outside the __init__ method and are the same for every instance of the class.

10
Example:
python
Copy code
class Car:
wheels = 4 # Class attribute

def __init__(self, make, model, year):


self.make = make
self.model = model
self.year = year

my_car = Car('Toyota', 'Corolla', 2020)


your_car = Car('Honda', 'Civic', 2018)

print(my_car.wheels) # Output: 4
print(your_car.wheels) # Output: 4
In this example, the wheels attribute is a class attribute, and it is shared across all instances of the
Car class. Both my_car and your_car have the same value for wheels.
Key Differences:
 Instance attributes are specific to each object.
 Class attributes are shared by all instances of the class.

3.3.3 Instance Methods


Instance methods are methods that operate on the instance attributes of a specific object. These
methods have access to the object’s data via the self keyword, which refers to the object calling
the method.
Example:
python
Copy code
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year

def start_engine(self):
print(f"The {self.year} {self.make} {self.model} engine starts.")

my_car = Car('Toyota', 'Corolla', 2020)


my_car.start_engine() # Output: The 2020 Toyota Corolla engine starts.
In this example, start_engine is an instance method that operates on the instance attributes of the
my_car object.

3.3.4 Static Methods


Static methods are methods that belong to the class rather than a specific instance of the class.
They do not have access to instance attributes (i.e., they do not use the self parameter). Static

11
methods are typically used for utility or helper functions that do not require access to object-
specific data.
In Python, static methods are created using the @staticmethod decorator.
Example:
python
Copy code
class MathOperations:
@staticmethod
def add(a, b):
return a + b

# Calling a static method


result = MathOperations.add(10, 5)
print(result) # Output: 15
In this example, add is a static method because it does not rely on any instance attributes. It can
be called directly on the class without needing an object instance.
Key Points:
 Static methods are used when a method does not need access to instance data.
 They are called directly on the class, not on an instance.

Chapter 4: Inheritance in Object-Oriented Programming (OOP)


Inheritance is one of the core principles of Object-Oriented Programming (OOP). It allows a
class (the subclass) to inherit properties (attributes) and methods (behaviors) from another class
(the superclass). This promotes code reuse, extensibility, and a logical structure where classes
can be related to each other in a hierarchical manner.
In this chapter, we will explore how inheritance works, how to implement it, and how method
overriding is used to modify or extend inherited behavior in subclasses.

4.1 Implementing Inheritance


Inheritance allows the creation of a new class (subclass) by reusing the code (attributes and
methods) of an existing class (superclass). The subclass inherits everything from the superclass,
meaning it can access and use the attributes and methods of the superclass while also adding new
features or modifying existing ones.
Inheritance is implemented by specifying the superclass name in parentheses after the subclass
name.
Syntax of Inheritance:
python
Copy code
class Superclass:
# Attributes and methods of the superclass

class Subclass(Superclass):
# Attributes and methods of the subclass
When a subclass inherits from a superclass:
 It automatically gets all the attributes and methods of the superclass.
 The subclass can add new attributes and methods, or override existing ones.

12
Example 1: Basic Inheritance
Let’s look at an example of inheritance where the subclass Car inherits from the superclass
Vehicle:
python
Copy code
class Vehicle:
def __init__(self, make, model):
self.make = make # Attribute: manufacturer of the vehicle
self.model = model # Attribute: model of the vehicle

def start(self):
print(f"Starting the {self.make} {self.model}")

def stop(self):
print(f"Stopping the {self.make} {self.model}")

class Car(Vehicle): # Car inherits from Vehicle


def honk(self):
print(f"{self.make} {self.model} honks!")
In this example:
 Vehicle is the superclass, which has attributes make and model, and methods start() and
stop().
 Car is the subclass that inherits from Vehicle. It can use the start() and stop() methods
defined in Vehicle, and it also adds a new method honk() that is specific to the Car class.
Example 2: Creating Objects from the Subclass
python
Copy code
# Creating an object of the Car class
my_car = Car('Toyota', 'Corolla')

# Using inherited methods from Vehicle


my_car.start() # Output: Starting the Toyota Corolla
my_car.stop() # Output: Stopping the Toyota Corolla

# Using a method defined in Car


my_car.honk() # Output: Toyota Corolla honks!
In this example, the object my_car is an instance of the Car class. It inherits the start() and stop()
methods from the Vehicle class, and it can also use its own honk() method.
4.2 Method Overriding
Method overriding occurs when a subclass provides its own implementation of a method that is
already defined in its superclass. The new implementation in the subclass overrides (replaces) the
implementation in the superclass.
This allows the subclass to inherit the method but adapt or customize its behavior to suit its own
needs. This is particularly useful when the subclass requires specific functionality that differs
from the superclass.

13
Syntax of Method Overriding:
To override a method, you simply define a method in the subclass with the same name and
parameters as the method in the superclass.
python
Copy code
class Superclass:
def method(self):
# Original implementation

class Subclass(Superclass):
def method(self):
# New, overridden implementation
When the method is called on an object of the subclass, the overridden version will be executed.
Example 1: Basic Method Overriding
Let’s extend the previous example by overriding the start() method in a subclass:
python
Copy code
class ElectricCar(Car): # ElectricCar is a subclass of Car
def start(self): # Overriding the start method
print(f"The electric {self.make} {self.model} starts silently.")
In this example:
 ElectricCar is a subclass of Car.
 It overrides the start() method inherited from Vehicle to provide a new implementation
specific to electric cars.
Example 2: Creating Objects from the Subclass with Method Overriding
python
Copy code
# Creating an object of the ElectricCar class
my_electric_car = ElectricCar('Tesla', 'Model S')

# Using overridden method


my_electric_car.start() # Output: The electric Tesla Model S starts silently

# Using inherited method


my_electric_car.honk() # Output: Tesla Model S honks!
In this example:
 The object my_electric_car is an instance of ElectricCar.
 When the start() method is called, the overridden version in ElectricCar is executed,
which prints a message indicating that the car starts silently.
 The honk() method is still inherited from the Car class, so calling it behaves just like it
did before.
Example 3: Calling the Superclass Method
In some cases, you may want to extend the functionality of a superclass method rather than
completely replacing it. To do this, you can call the superclass method using the super() function
within the overridden method.
python

14
Copy code
class ElectricCar(Car):
def start(self):
super().start() # Call the start method from the Vehicle class
print(f"The electric {self.make} {self.model} makes no noise when starting.")
Here:
 The super().start() call invokes the start() method from the superclass Vehicle, so the
original functionality is preserved.
 After calling the superclass method, the ElectricCar adds its own message about starting
silently.
Types of Inheritance
Inheritance in OOP can take several forms, depending on how classes relate to each other:
1. Single Inheritance: A subclass inherits from one superclass.
o Example: Car inherits from Vehicle.
2. Multiple Inheritance: A subclass inherits from multiple superclasses (supported in some
programming languages like Python).
o Example: FlyingCar might inherit from both Vehicle and Airplane.
3. Multilevel Inheritance: A subclass inherits from another subclass, creating a chain of
inheritance.
o Example: ElectricCar inherits from Car, which inherits from Vehicle.
4. Hierarchical Inheritance: Multiple subclasses inherit from the same superclass.
o Example: Car, Truck, and Motorcycle all inherit from Vehicle.
4.3 Advantages of Inheritance
1. Code Reusability: Inheritance promotes the reuse of code. The subclass can inherit
methods and attributes from the superclass without duplicating code.
2. Extensibility: New features and behaviors can be added to a subclass without modifying
the original superclass, allowing for flexible and scalable systems.
3. Modularity: By organizing code into a hierarchy of classes, inheritance helps in breaking
down complex problems into simpler, related components.
4. Polymorphism: Inheritance allows polymorphism, which means that a subclass can
define methods that share the same name as methods in the superclass but behave
differently. This increases flexibility and dynamism in code execution.

# Chapter 5: Polymorphism in Object-Oriented Programming (OOP)

Polymorphism is a key concept in Object-Oriented Programming that allows objects of different


classes to be treated as objects of a common superclass. It provides a way to perform a single
action in different forms. There are two primary types of polymorphism in OOP: method
overloading (compile-time polymorphism) and method overriding (run-time polymorphism).
This chapter delves into both forms of polymorphism, explaining their implementations and uses
in programming.
5.1 Method Overloading (Compile-time Polymorphism)
Method Overloading allows multiple methods in the same class to have the same name but differ
in the number or type of parameters. This enhances code readability and allows different ways to
invoke the same method based on the context of the parameters provided.

15
In languages like Python, method overloading is not natively supported in the same way as in
Java or C++. Instead, developers typically achieve similar functionality by using default
arguments or variable-length arguments. However, for educational purposes, we can illustrate
how method overloading would look conceptually.

# Example of Method Overloading

```python
class Calculator:
def add(self, a, b): # Method with 2 parameters
return a + b

def add(self, a, b, c): # Method with 3 parameters


return a + b + c

# This will result in an error or unexpected behavior


calc = Calculator()
print(calc.add(2, 3)) # Error: add() missing 1 required positional argument
```

In the example above, the second `add` method definition effectively overrides the first due to
the same method name, resulting in a conflict. To properly implement method overloading in
Python, you would use default parameters or variable-length arguments:

```python
class Calculator:
def add(self, *args): # Accepts variable-length arguments
return sum(args)

calc = Calculator()
print(calc.add(2, 3)) # Outputs: 5
print(calc.add(2, 3, 4)) # Outputs: 9
```

Here, the `add` method can handle any number of arguments, providing a polymorphic behavior
that adapts based on the input.

5.2 Method Overriding (Run-time Polymorphism)


Method Overriding occurs when a subclass provides a specific implementation of a method that
is already defined in its superclass. This allows the subclass to modify or extend the behavior of
the superclass's method.
In method overriding, the method in the subclass must have the same name, return type, and
parameters as the method in the superclass.

# Example of Method Overriding

16
```python
class Animal:
def sound(self):
return "Some sound" # General sound

class Dog(Animal):
def sound(self): # Overriding the sound method
return "Bark" # Specific sound for Dog

class Cat(Animal):
def sound(self): # Overriding the sound method
return "Meow" # Specific sound for Cat

# Demonstrating method overriding


animals = [Dog(), Cat()]

for animal in animals:


print(animal.sound()) # Outputs: Bark, Meow
```

In this example:
- The `Animal` class defines a general `sound` method.
- Both `Dog` and `Cat` classes override the `sound` method to provide specific implementations.
- When iterating through a list of `Animal` objects, the overridden method in each subclass is
invoked, demonstrating run-time polymorphism.

---
# Chapter 6: Encapsulation and Data Hiding
Encapsulation is another fundamental principle of OOP that involves bundling data (attributes)
and methods (functions) that operate on the data into a single unit known as a class. This concept
also restricts direct access to some of an object’s components, leading to greater data integrity
and abstraction.
6.1 Implementing Encapsulation
Encapsulation is often implemented by making attributes private and providing public methods
to modify or access these attributes. By doing this, we control how the data is accessed and
modified.
# Example of Encapsulation

```python
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Private attribute

def deposit(self, amount):


self.__balance += amount # Method to modify the private attribute

17
def get_balance(self):
return self.__balance # Method to access the private attribute

# Usage
account = BankAccount(1000)
account.deposit(500)
print(account.get_balance()) # Output: 1500
```
In this example:
- The `__balance` attribute is private, denoted by the double underscores, meaning it cannot be
accessed directly from outside the class.
- The methods `deposit` and `get_balance` provide controlled access to modify and retrieve the
balance.
6.2 Access Modifiers

Access modifiers are keywords used to specify the accessibility of class members. In Python, we
commonly use three access modifiers:

- Private: Attributes and methods that cannot be accessed from outside the class (e.g.,
`__attribute`).
- Protected: Attributes and methods intended to be accessible only within the class and its
subclasses (e.g., `_attribute`).
- Public: Attributes and methods that are accessible from outside the class.

Example of access modifiers:


```python
class Example:
def __init__(self):
self.public_attribute = "I am public" # Public
self._protected_attribute = "I am protected" # Protected
self.__private_attribute = "I am private" # Private

example = Example()
print(example.public_attribute) # Accessible
print(example._protected_attribute) # Accessible, but should be treated as private
# print(example.__private_attribute) # Raises an AttributeError
```

# Chapter 7: Benefits and Drawbacks of OOP

8.1 Benefits of OOP

1. Modularity: OOP promotes organizing code into separate objects, making it easier to manage
and understand.

18
2. Code Reusability: Classes and objects can be reused across different projects, reducing
duplication and development time.

3. Scalability: New features can be added by creating new classes or extending existing ones,
making systems more flexible and scalable.

4. Maintainability: The modular nature of OOP makes it easier to update and maintain code, as
changes can be made in isolation without affecting other components.

8.2 Drawbacks of OOP

1. Complexity: OOP can introduce additional complexity compared to procedural programming,


especially for beginners who may find the concepts of classes and objects challenging.

2. Performance: OOP can lead to performance overhead due to abstraction layers and the added
complexity of managing objects and their interactions.

3. Overhead: Maintaining multiple classes and objects can sometimes lead to increased memory
usage and performance issues, particularly in large-scale applications.

19

You might also like