0% found this document useful (0 votes)
7 views38 pages

Module5 Notes-1

This document provides an overview of classes and objects in Python, detailing how to define classes, create instances, and access class members. It covers key concepts such as the __init__ method, mutability, copying objects, operator overloading, and inheritance. Examples are included to illustrate the practical application of these concepts in object-oriented programming.

Uploaded by

ajayishereaj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views38 pages

Module5 Notes-1

This document provides an overview of classes and objects in Python, detailing how to define classes, create instances, and access class members. It covers key concepts such as the __init__ method, mutability, copying objects, operator overloading, and inheritance. Examples are included to illustrate the practical application of these concepts in object-oriented programming.

Uploaded by

ajayishereaj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 38

MODULE – 5

Classes and objects: Programmer-defined types, Attributes, Rectangles,


Instances as return values, Objects are mutable, Copying,

Classes and functions: Time, Pure functions, Modifiers, Prototyping versus


planning,

Classes and methods: Object-oriented features, Printing objects, Another


example, A more complicated example, The init method, The __str__ method,
Operator overloading, Type-based dispatch, Polymorphism, Interface and
implementation
what is class? how to define a class in Python? How to initiate a class and how
the class members are accessed?

What is a Class?

• In Python, a class is a blueprint for creating objects (instances).


• A class defines the properties (attributes) and behaviours (methods)
that its instances(object) will have.

Defining a Class in Python

• To define a class, use the “class” keyword followed by the name of the
class (typically written in PascalCase).

syntax:

class ClassName:

def __init__(self, attribute1, attribute2):

self.attribute1 = attribute1

self.attribute2 = attribute2

def method_name(self):

# Code for the method

pass

• __init__ is the constructor method. It is automatically called when you


create an object (instance) of the class. This method is used to initialize
the attributes of the class.

• self refers to the current instance(object) of the class.

• It is used to access attributes that belong to the class and methods within
the class.
Example:

class Car:

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

self.brand = brand

self.model = model

self.year = year

def display_info(self):

print(f"This car is a {self.year} {self.brand} {self.model}.")

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

In this example:

• Car is the class.

• The __init__ method initializes the class with three attributes: brand,
model, and year.

• display_info is a method that displays information about the car.

Creating an Object (Instantiating a Class)

Once you have defined a class, you can create instances (objects) of that class
by calling the class as if it were a function, passing the required arguments to
the __init__ method.

object_name = class name()# (arguments)

my_car = Car("Toyota", "Corolla", 2020) #

In this example, my_car is an instance of the Car class, with the brand
"Toyota", model "Corolla", and year 2020.
Accessing Class Members

There are two types of class members:

1. Attributes: These are the data values associated with an instance of the
class.

2. Methods: These are functions that define behaviours or actions related to


the instance of the class.

Accessing Attributes

You can access the attributes of an instance using the dot (.) notation:

print(my_car.brand) # Output: Toyota

print(my_car.model) # Output: Corolla

print(my_car.year) # Output: 2020

Accessing Methods

You can also call methods using the dot notation:

my_car.display_info() # Output: This car is a 2020 Toyota Corolla.

3. Rectangles (A Class Example)

Example: Define a Rectangle Class

class Rectangle:

def __init__(self, width, height):

self.width = width

self.height = height
This creates a class with two attributes:

• width
• height

Each Rectangle object will have its own width and height.

Creating Rectangle Objects:

r1 = Rectangle(10, 5)

r2 = Rectangle(4, 7)

Here,

• r1.width = 10, r1.height = 5


• r2.width = 4, r2.height = 7

Add Behavior: Methods (e.g., area, perimeter)

class Rectangle:

def __init__(self, width, height):

self.width = width

self.height = height
def area(self):

return self.width * self.height

def perimeter(self):

return 2 * (self.width + self.height)

Usage of Methods:

r1 = Rectangle(10, 5)

print("Area:", r1.area()) # Output: 50

print("Perimeter:", r1.perimeter()) # Output: 30

Summary Table:

Component Meaning

__init__() Initializes new objects


self.width Object-specific attribute

area() Method to calculate area using attributes


r1.area() Call method on object r1
Instances as Return Values

What Does This Mean?

In Python, a function can return an object (instance) of a class.

This is very useful when:

• You want to create objects inside a function.


• You want to hide complexity and return a ready-to-use object.

Example:

Let’s extend the Rectangle class from earlier.

class Rectangle:

def __init__(self, width, height):

self.width = width

self.height = height

def area(self):

return self.width * self.height

Now, define a function that returns a Rectangle object:

def create_square(side_length):

return Rectangle(side_length, side_length)


Usage:

sq = create_square(6)

print("Square area:", sq.area()) # Output: 36

Here:

• create_square() is a function that returns a Rectangle instance.


• sq is now an object of the Rectangle class.
• It behaves like any other rectangle.

Benefit Explanation
Can reuse the same function to create many
Code reuse
instances

Cleaner design User doesn't need to know class details

Can return different Helpful in advanced object-oriented


subclasses programming

Another Example: Returning a Student Object

class Student:

def __init__(self, name, roll_no):

self.name = name
self.roll_no = roll_no

def create_student():

return Student("Hithesh", 101)

s1 = create_student()

print(s1.name) # Output: Hithesh

Key Takeaways:

Concept Description

Function returns instance Using return ClassName(...)

Access like any object After return, use dot notation normally

Cleaner code Makes object creation reusable and modular

Objects are Mutable

What Does Mutable Mean?

Mutable means changeable.


If an object is mutable, you can modify its attributes after it has been
created.

In Python, most built-in types like list, dict, and custom class instances (like
Student, Rectangle) are mutable.

Example:

Let’s use our Rectangle class again:

class Rectangle:

def __init__(self, width, height):

self.width = width

self.height = height

def area(self):

return self.width * self.height

Now create and modify an object:

r1 = Rectangle(10, 5)

print("Area:", r1.area()) # Output: 50

# Change attributes

r1.width = 20

print("New Area:", r1.area()) # Output: 100


• We changed the width from 10 to 20.
• The object remembers the change, and it affects future behavior.

Why Does Mutability Matter?

1. Efficient Memory Use:


o Instead of creating a new object, we can update the existing one.
2. Function Behavior:
o If a function modifies an object passed to it, the original object will
change.

Summary Table:

Term Meaning

Mutable Can be changed after creation

Implies Attribute values can be updated

Impact Changes inside a function affect the object

Examples Classes, lists, dictionaries (mutable)

Non-mutable int, str, tuple (these can't change)


Copying Objects

What Does It Mean?

Copying an object means creating a new object with the same attributes or
data as an existing one.

But there are two ways this can happen in Python:

Type of Copy Description


Shallow Copy Copies the reference (pointer), not actual data
Deep Copy Copies everything (a completely new object)

Default Assignment is Not Copying

r1 = Rectangle(10, 5)

r2 = r1 # Not a real copy!

r2.width = 20

print(r1.width) # Output: 20 (because r1 and r2 are same object)

• r2 is just another name for the same object.


• Any change to r2 affects r1.
Real Copy Using copy Module

import copy

class Rectangle:

def __init__(self, width, height):

self.width = width

self.height = height

r1 = Rectangle(10, 5)

r2 = copy.copy(r1) # Shallow copy

r2.width = 20

print("r1.width:", r1.width) # Output: 10

print("r2.width:", r2.width) # Output: 20

• r2 is a new object.
• Changes to r2 do not affect r1.

Deep Copy Example

Deep copy is used when your object contains other mutable objects inside it
(like lists).
class Box:

def __init__(self, items):

self.items = items

b1 = Box(["apple", "banana"])

b2 = copy.deepcopy(b1)

b2.items.append("cherry")

print(b1.items) # Output: ['apple', 'banana']

print(b2.items) # Output: ['apple', 'banana', 'cherry']

Without deepcopy(), both boxes would share the same list.

Key Differences:

Copy Type Description When to Use

Just another name for the


Assignment Never for true copies
same object

New object, but shallow


copy.copy() Safe for simple objects
inside

Needed if object contains lists


copy.deepcopy() Fully independent copy
or other objects
(Imagine photocopying a form:

• A shallow copy is like sharing the same form with someone.


• A deep copy is like making a new copy, and each person writes
independently.)

Summary:

• Use the copy module when you want to duplicate objects.


• Shallow copy is fine for simple data.
• Deep copy is important when objects contain other objects inside them.

_ _init_ _ method

➢ In Python, the `__init__` method is a special method used for initializing


newly created objects.
➢ It's called a constructor method because it initializes the object when it
is created.
➢ The __init__ method is defined within a class and is automatically
called when a new object of the class is created.
example:

class MyClass:

def _ _init_ _ (self, x, y):

self.x = x

self.y = y

# Creating an object/instance of MyClass

obj = MyClass(10, 20)

print(obj.x)

print(obj.y)

Output: 10

Output: 20

In this example, the `__init__` method takes three parameters: `self`, `x`, and
`y`.

`self` refers to the instance/object itself and is automatically passed when the
method is called.

The `x` and `y` parameters are used to initialize the instance variables `x` and
`y` respectively.

When we create an object of `MyClass` (`obj`), the `__init__` method is


automatically called with the arguments `(10, 20)`, so `obj.x` is set to `10` and
`obj.y` is set to `20`.
The `__init__` method can be used to perform any necessary initialization tasks,
such as initializing instance variables, setting up default values, or performing
any other setup actions required when an object is created.

The _ _ str_ _ method

In Python, the `__str__` method is a special method used to define the string
representation of an object.

When you use built-in functions like `str()` or `print()` on an object, Python
calls the object's `__str__` method to get its string representation.

Here's a simple example to illustrate how `__str__` works:

class MyClass:

def _ _init_ _(self, x, y):

self.x = x

self.y = y

def _ _str_ _(self):

return f"MyClass: x={self.x}, y={self.y}"

# Creating an object of MyClass

obj = MyClass(10, 20)

# Using print() on the object

print(obj)

Output:

MyClass: x=10, y=20


In this example, the `_ _str_ _` method is defined within the `MyClass` class .

It returns a string that represents the state of the object.

When `print(obj)` is called, Python internally calls `obj._ _str_ _()` to get the
string representation of the object, which is then printed.

You can customize the string representation returned by `__str__` to provide


any information about the object that you find useful. This method is often used
to provide a human-readable representation of objects, making debugging and
understanding code easier.

Operator overloading

Operator overloading in Python refers to the ability to define custom behaviour


for operators when they are used with user-defined objects.

This means that you can redefine the meaning of operators such as `+`, `-`, `*`,
`/`, `==`, `!=`, `<`, `>`, and many others for your own classes.

For example, you can define what it means to add two instances of a custom
class together, or how to compare them for equality.

Here's a simple example demonstrating operator overloading in Python:


Example:

class Point:

def _ _init_ _(self, x, y):

self.x = x

self.y = y

def _ _add_ _ (self, other):

# Define addition behaviour for Point objects

return Point(self.x + other.x, self.y + other.y)

def _ _str_ _(self):

# Define string representation for Point objects

return f"({self.x}, {self.y})"

# Creating two Point objects

point1 = Point(1, 2)

point2 = Point(3, 4)

# Adding two Point objects

result = point1 + point2 # it will call __add__ method

print(result) # it will call __str__ method

# Output: (4, 6)

In this example:

- The `_ _add_ _` method is defined to override the behavior of the `+` operator
for `Point` objects.
When two `Point` objects are added together using the `+` operator (`point1 +
point2`), the `_ _add_ _` method is called, and the result is a new `Point` object
with coordinates that are the sum of the corresponding coordinates of the
operands.

- The `__str__` method is defined to provide a string representation of `Point`


objects when they are printed or converted to a string.

By implementing these special methods, you can define custom behavior for
operators in your classes, allowing them to behave naturally in Python
expressions and operations.
Inheritance

➢ Inheritance is a fundamental concept in OOP that allows us to create a


new class based on an existing class.
➢ The new class inherits attributes and behaviours from the existing
class, and it can also add its unique attributes and behaviours.
➢ This promotes code reuse and allows for building a hierarchy of
classes, which is particularly useful when modelling objects that share
common characteristics.
Eg:

# Super class/Parent class

class Animal:

def __init__(self, name):

self.name = name

def speak(self):

print("{} makes a sound".format(self.name))

# Sub class/Child class (inherits from Animal)

class Dog(Animal):

def speak(self):

print("{} barks".format(self.name))

# Creating an object of the child class

dog1 = Dog("Tommy")

dog1.speak()

Output:

Tommy barks

Explanation:

1. Class Animal:

o This is the parent class (also called the base class).

o It has:
▪ An __init__() method (constructor) that accepts name and
assigns it to self.name.

▪ A method speak() that prints:


"Tommy makes a sound" using .format().

2. Class Dog:

o This is a child class, created by inheriting from Animal using class


Dog(Animal):.

o It inherits the __init__() method from Animal, so you don’t have to


write it again.

o It overrides the speak() method to provide a specific message:


"Tommy barks".

3. Object Creation:

o dog1 = Dog("Tommy") creates an object dog1 of class Dog.

o "Tommy" is passed to the constructor, which sets self.name =


"Tommy".

4. Method Call:

o dog1.speak() calls the speak() method from the Dog class (not the
one in Animal).

o So, it prints: Tommy barks.

Inheritance promotes code reuse and simplifies the design of your program by
allowing you to create specialized classes that inherit common attributes and
behaviors from a base class. This makes your code more maintainable and
reduces redundancy.
Polymorphism

Polymorphism is a fundamental concept in object-oriented programming (OOP)


that refers to the ability of different objects to respond to the same message
or method call in different ways.

It allows functions, methods or operators to behave differently based on the


type of data they are handling. Derived from Greek, Polymorphism means
“Many forms”.

In Python, polymorphism is achieved through method overriding and method


overloading.

1. Method Overriding:

When a subclass provides a specific implementation of a method that is


already defined in its superclass, it is called method overriding. The method
in the subclass "overrides" the implementation of the method in the
superclass. When the method is called on an instance of the subclass, the
overridden method in the subclass is executed.
Example:

class Animal: # super class

def speak(self):

print("Animal speaks")

class Dog(Animal): # subclass

def speak(self):

print("Dog barks")

# Creating instances

animal = Animal()

dog = Dog()

# Polymorphism

animal.speak()

Output: Animal speaks

dog.speak()

Output: Dog barks


2. Method Overloading:

In Python, method overloading is not directly supported as it is in some other


languages like Java. However, you can achieve a form of method
overloading using default arguments or variable-length arguments.

Default arguments:

This allows a single method to behave differently based on the number


or type of arguments passed to it.

Example using default arguments:

class Calculator:

def add(self, a, b=0):

return a + b

calc = Calculator()

print(calc.add(5))

Output: 5

print(calc.add(2, 3))

Output: 5
Example using variable-length arguments:

class Calculator:

def add(self, *args): # arbitrary arguments

return sum(args)

calc = Calculator()

print(calc.add(5))

Output: 5

print(calc.add(2, 3))

Output: 5

print(calc.add(1, 2, 3, 4))

Output: 10

In both examples, the `add` method behaves differently based on the number
of arguments passed to it, demonstrating polymorphic behaviour.

Polymorphism helps in writing flexible and reusable code by allowing different


objects to be treated uniformly through a common interface, even though they
may behave differently. This promotes code reuse and simplifies the design of
software systems.
def histogram(s):

d = dict()

for c in s:

if c not in d:

d[c] = 1

else:

d[c] = d[c]+1

return d

t = ['spam', 'egg', 'spam', 'spam', 'bacon', 'spam']

histogram(t)

Type-Based Dispatch

Type-based dispatch in Python is a way to change how a function behaves


based on the type of input it receives.

It's useful in data science, where different input types might need to be
processed by the same function.

Working.

• Type-based dispatch maps types from type annotations to functions.

• When a function is called with input, the type-based dispatch system


determines which function to call based on the input type.
• This ensures that the correct function is called for each input.

• You can use the TypeDispatch class in Python to implement type-based


dispatch.

• You can also use the @overload annotation to support multiple dispatch.

• Type-based dispatch can improve documentation and avoid code


repetition.

• It can create a common API for functions that perform similar tasks.

Example:

def process(value):

if isinstance(value, int):

return f"Integer: {value * 2}"

elif isinstance(value, str):

return f"String: {value[::-1]}" # Reverses the string

elif isinstance(value, list):

return f"List: {[item * 2 for item in value]}"

else:

return "Unsupported type!"

# Test cases

print(process(10)) # Output: Integer: 20

print(process("hello")) # Output: String: olleh

print(process([1, 2, 3])) # Output: List: [2, 4, 6]

print(process(3.14)) # Output: Unsupported type!


The function uses isinstance to check the type of the input argument.

Interface and implementation

One of the goals of object-oriented design is to make software more


maintainable, which means that you can keep the program working when
other parts of the system change, and modify the program to meet new
requirements.

A design principle that helps achieve that goal is to keep interfaces separate
from implementations.

For objects, that means that the methods a class provides should not depend
on how the attributes are represented.

Interface defines the "what" (expected methods and behaviours).

Implementation defines the "how" (actual logic and functionality).

What Is an Interface?

An interface defines what an object can do — not how it does it.

What Is Implementation?

Implementation is the actual code or logic written inside the class methods to
fulfill the interface.
Concept Real-world Analogy

Interface A TV remote's buttons (what user sees/uses)

Implementation Internal circuit of the TV (how it works)

The user presses a button (interface), not knowing how signals are sent
(implementation).

Python Example:

Let’s create a class that behaves like a shape (e.g., rectangle or circle):

class Shape:

def area(self):

pass # interface method

def perimeter(self):

pass # interface method

This class defines the interface (a set of method names that other classes must
follow).

Now, let’s implement it:

class Rectangle(Shape):

def __init__(self, width, height):

self.width = width

self.height = height
def area(self):

return self.width * self.height

def perimeter(self):

return 2 * (self.width + self.height)

Using Interface and Implementation:

def print_shape_info(shape):

print("Area:", shape.area())

print("Perimeter:", shape.perimeter())

rect = Rectangle(5, 4)

print_shape_info(rect)

• print_shape_info() works with any object that implements the shape


interface.

• We don’t care if it’s a Rectangle, Circle, or Hexagon.

• As long as it has .area() and .perimeter() methods, it works.


Aspect Interface Implementation
Definition Specifies the methods a Provides the actual
class must implement. functionality of the
methods.
Purpose Defines what actions are Defines how those
expected. actions are performed.
Focus Focuses on abstraction. Focuses on concrete
behavior.
Reusability Promotes code Implements specific
reusability and logic for different use
consistency. cases.

Keeping the interface separate from the implementation means that you have to
hide the attributes.

Code in other parts of the program (outside the class definition) should use
methods to read and modify the state of the object.

They should not access the attributes directly. This principle is called
information hiding;

Pure functions and Modifiers.

Pure functions

A pure function is a function that satisfies two key properties:

• The function always produces the same output for the same input.

• The function does not modify any external state or variable outside its
scope.
In simple terms, a pure function always returns the same result when called with
the same arguments and does not alter any external variables or state (such as
global variables, attributes).

Example of Pure Function

class Time:

def __init__(self, hours, minutes):

self.hours = hours

self.minutes = minutes

def total_minutes(self):

return self.hours * 60 + self.minutes

total_minutes() is pure because:

• It doesn't change the object

• It just returns a computed result

Advantages of Pure Functions:

• Easier to test and debug.

• Can be parallelized, as they don’t rely on shared mutable state.

• Enhances code readability and maintainability.


Modifiers

In Python, modifiers generally refer to methods that modify the state of an


object or class.

These are methods that change the value of an object's attributes or perform
operations that alter the state.

They are commonly referred to as "mutators" or "setters" when they modify


instance attributes.

Example

class Time:

def __init__(self, hours, minutes):

self.hours = hours

self.minutes = minutes

def add_minutes(self, mins):

self.minutes += mins

while self.minutes >= 60:

self.hours += 1

self.minutes -= 60

add_minutes() is a modifier

It changes the state of the object (updates hours and minutes)


Prototyping vs Planning

Prototyping and Planning are two distinct approaches to software


development, each with its own characteristics, benefits, and challenges.

Definition

• Prototyping:

o Involves creating a working model (prototype) of the software


system early in the development process.

o This model is used to gather feedback and improve the design


iteratively.

o It is often used when the requirements are unclear or


changing/evolving.

• Planning:

o Refers to creating a detailed and comprehensive plan before


starting the development process.

o It includes defining the project scope, timelines, resources, and


requirements in advance, aiming to have a clear and fixed vision of
the end product before starting work.

Aspect Prototyping Planning

Creating a preliminary version Outlining the detailed steps,


Definition or model of a product to explore objectives, and resources for
ideas or test functionality. executing a project.
Aspect Prototyping Planning

To experiment, validate ideas, To establish a structured


Purpose and identify potential issues roadmap to achieve the
early in development. project's goals efficiently.

Emphasis on organization,
Emphasis on experimentation
Focus resource allocation, and
and iterative improvement.
setting clear goals.

Early stages of development or Before execution or as an


Stage of Use when exploring solutions to a ongoing process to guide the
problem. project.

High level of detail,


Minimal detail; focuses on including timelines,
Level of Detail
functionality or core features. milestones, and resource
management.

Project management tools,


Sketches, wireframes, mock-
Tools Used Gantt charts, budgets, and
ups, or working models.
documentation.

Less flexible; changes


Highly flexible; allows changes
Flexibility require updates to plans and
and experimentation.
timelines.
A tangible representation or A comprehensive plan that
Outcome proof of concept to validate serves as a blueprint for
ideas. execution.
Focused on aligning
Encourages feedback and
Stakeholder expectations and
collaboration from stakeholders
Involvement responsibilities before
during iterations.
project execution.
Aspect Prototyping Planning

Short-term; used to quickly Long-term; outlines the full


Timeframe
explore and refine ideas. duration of the project.

• Both approaches often complement each other in product development.


• Prototyping can inform and refine planning, while planning ensures the
prototype fits into the larger project goals.

You might also like