Popular Searches
Popular Course Categories
Popular Courses

Understanding Classes and Objects

Understanding Classes and Objects

Object-Oriented Programming in Python

Understanding Classes and Objects in Python

Course: Python Training

Topic: Understanding Classes and Objects

Course Link: Python Training Course

Demo Link: Register for a Course Demo

Website: JustAcademy


1. Introduction to Classes and Objects

Classes and objects are fundamental concepts in Object-Oriented Programming (OOP). Python supports object-oriented programming, allowing developers to organize data and functionality into reusable structures.

A class defines the attributes and behaviors that its objects can have. An object is an instance created from a class. Objects can store their own data and use methods defined by their class.

For example, consider a student management application. Every student may have a name, age, course, and marks. Instead of creating separate variables for every student, we can define a Student class and create multiple student objects from it.

class Student:
    pass

student1 = Student()
student2 = Student()

print(type(student1))
print(type(student2))

Output:

<class '__main__.Student'>
<class '__main__.Student'>

The Student class defines a new type. The variables student1 and student2 refer to two separate instances of that class.

2. What Is a Class?

A class is a blueprint or template used to create objects. It groups related data and functions into a single structure.

A class commonly contains:

  • Attributes: Variables that store information about a class or its objects.
  • Methods: Functions defined inside a class that describe operations or behaviors.
  • Constructor-related initialization: Code that prepares a new object with its initial state.

2.1 Basic Class Syntax

class ClassName:
    # Attributes and methods
    pass

The class keyword begins a class definition. ClassName is the name of the class, and the indented block contains its body. The pass statement is a placeholder that allows the class body to remain empty.

2.2 Example of a Simple Class

class Car:
    brand = "Toyota"
    color = "White"

car1 = Car()

print(car1.brand)
print(car1.color)

Output:

Toyota
White

In this example, Car is a class, and car1 is an instance of it. The attributes brand and color are defined in the class body and can be accessed through the object.

3. What Is an Object?

An object is a particular instance of a class. When an object is created, it can hold data describing that specific instance and use the methods available through its class.

For example, a Car class can be used to create many car objects. Each object can represent a different car with its own brand, color, and model.

class Car:
    pass

car1 = Car()
car2 = Car()
car3 = Car()

print(car1)
print(car2)
print(car3)

Python displays object representations that commonly include the class name and an implementation-specific memory address. The objects are separate instances even though they come from the same class.

3.1 Real-World Examples

Class Possible Attributes Possible Methods
Student Name, age, course, marks Display details, calculate grade
Car Brand, model, speed Accelerate, brake, display details
BankAccount Account holder, balance Deposit, withdraw, check balance
Employee Name, employee ID, salary Display profile, calculate bonus
Product Name, price, quantity Calculate total, update stock
Book Title, author, price Display information, update price

4. Creating Objects from a Class

Objects are created by calling the class name as though it were a function. This process is known as instantiation.

Syntax:

object_name = ClassName()

4.1 Example

class Student:
    name = "Aarav"
    course = "Python"

student1 = Student()

print(student1.name)
print(student1.course)

Output:

Aarav
Python

Calling Student() creates an instance of the class. The object can then access its attributes using dot notation.

4.2 Creating Multiple Objects

class Employee:
    company = "ABC Technologies"

employee1 = Employee()
employee2 = Employee()

employee1.name = "Riya"
employee2.name = "Karan"

print(employee1.name)
print(employee2.name)
print(employee1.company)
print(employee2.company)

Output:

Riya
Karan
ABC Technologies
ABC Technologies

Each employee object has its own dynamically assigned name attribute. Both objects can access the shared class attribute company.

5. Understanding the __init__() Method

The __init__() method initializes a newly created object. It is commonly used to assign starting values to instance attributes.

When you call a class to create an object, Python creates the instance and then invokes its initialization method. Technically, __init__() initializes an instance; it is not the method that creates the instance itself.

5.1 Syntax

class ClassName:
    def __init__(self, parameter1, parameter2):
        self.attribute1 = parameter1
        self.attribute2 = parameter2

5.2 Example

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

student1 = Student("Aarav", 20)
student2 = Student("Meera", 22)

print(student1.name, student1.age)
print(student2.name, student2.age)

Output:

Aarav 20
Meera 22

The constructor parameters provide initial values for each object. The first student stores the name Aarav and age 20, while the second student stores different values.

5.3 Why Use __init__()?

  • It initializes object attributes automatically.
  • It avoids repeating attribute assignments after every object is created.
  • It makes the required data for an object clear.
  • It helps ensure objects start in a consistent state.

6. Understanding self in Python

self refers to the current instance of a class. It allows an instance method to access or modify that particular object's attributes and call its other methods.

In Python, self is a conventional parameter name, not a reserved keyword. Instance methods normally declare it as their first parameter. When a method is called through an object, Python supplies the instance automatically.

class Student:
    def __init__(self, name):
        self.name = name

    def display(self):
        print("Student name:", self.name)

student1 = Student("Ananya")
student1.display()

Output:

Student name: Ananya

When student1.display() is called, Python passes student1 as the instance argument. Inside the method, self.name accesses that object's name.

6.1 Different Objects Have Different self Values

class Mobile:
    def __init__(self, brand, price):
        self.brand = brand
        self.price = price

    def show_details(self):
        print(self.brand, self.price)

mobile1 = Mobile("Samsung", 25000)
mobile2 = Mobile("Apple", 65000)

mobile1.show_details()
mobile2.show_details()

Output:

Samsung 25000
Apple 65000

The same method works with both objects, but self refers to the instance on which the method is called.

7. Understanding Attributes in Python Classes

Attributes are named values associated with a class or an object. Python commonly uses two categories of attributes: instance attributes and class attributes.

7.1 Instance Attributes

Instance attributes belong to individual objects. They are commonly created using self inside __init__().

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

employee1 = Employee("Riya", 40000)
employee2 = Employee("Karan", 50000)

print(employee1.name, employee1.salary)
print(employee2.name, employee2.salary)

Output:

Riya 40000
Karan 50000

Each object maintains its own name and salary attributes.

7.2 Class Attributes

Class attributes are defined in the class body and are shared through the class unless an instance overrides the same attribute name.

class Employee:
    company = "Tech Solutions"

    def __init__(self, name):
        self.name = name

employee1 = Employee("Riya")
employee2 = Employee("Karan")

print(employee1.company)
print(employee2.company)
print(Employee.company)

Output:

Tech Solutions
Tech Solutions
Tech Solutions

Both objects can access the class attribute company. It can also be accessed directly through the class.

7.3 Instance Attributes vs Class Attributes

Feature Instance Attribute Class Attribute
Defined commonly in __init__() using self Class body
Associated with Individual object Class and its instances through attribute lookup
Typical use Unique names, prices, ages Shared company name, category, configuration
Access object.attribute Class.attribute or object.attribute
Changing an instance value Usually affects that instance only Assigning through an instance may create an instance attribute that shadows the class attribute

7.4 A Common Class Attribute Mistake

class Team:
    members = []

team1 = Team()
team2 = Team()

team1.members.append("Asha")

print(team1.members)
print(team2.members)

Output:

['Asha']
['Asha']

Both objects refer to the same mutable list stored as a class attribute. If each object needs its own list, initialize it in __init__():

class Team:
    def __init__(self):
        self.members = []

team1 = Team()
team2 = Team()

team1.members.append("Asha")

print(team1.members)
print(team2.members)

Output:

['Asha']
[]

8. Defining Methods Inside a Class

A method is a function defined inside a class. Methods describe the actions that objects can perform. An instance method usually accepts self as its first parameter.

8.1 Example of an Instance Method

class Calculator:
    def add(self, a, b):
        return a + b

    def subtract(self, a, b):
        return a - b

calculator = Calculator()

print(calculator.add(10, 5))
print(calculator.subtract(10, 5))

Output:

15
5

The methods are accessed using dot notation. In this example, the operations do not need to read instance attributes, but they are still valid instance methods.

8.2 Methods That Use Object Data

class BankAccount:
    def __init__(self, holder, balance):
        self.holder = holder
        self.balance = balance

    def display_balance(self):
        print(self.holder, "has balance", self.balance)

account = BankAccount("Rahul", 5000)
account.display_balance()

Output:

Rahul has balance 5000

The method accesses the object's attributes through self, allowing each bank account to display its own information.

9. Creating and Calling Methods with Parameters

Methods can accept parameters just like ordinary functions. Parameters allow an object to perform actions using values supplied when the method is called.

class Rectangle:
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        return self.length * self.width

    def perimeter(self):
        return 2 * (self.length + self.width)

rectangle = Rectangle(10, 5)

print("Area:", rectangle.area())
print("Perimeter:", rectangle.perimeter())

Output:

Area: 50
Perimeter: 30

The area() and perimeter() methods use the object's stored dimensions rather than requiring the dimensions to be passed again.

10. Modifying Object Attributes

Instance attributes can be modified after an object is created. Updating an attribute changes the state of that particular object.

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

product1 = Product("Keyboard", 1200)

print(product1.price)

product1.price = 1500

print(product1.price)

Output:

1200
1500

The assignment product1.price = 1500 updates the price stored on that object.

10.1 Updating Attributes Through a Method

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def update_price(self, new_price):
        if new_price >= 0:
            self.price = new_price
        else:
            print("Price cannot be negative")

product = Product("Mouse", 700)
product.update_price(850)

print(product.price)

Output:

850

Using a method for updates provides a place to validate input and maintain the object's expected state.

11. Accessing and Deleting Attributes

Python provides built-in functions such as getattr(), setattr(), hasattr(), and delattr() for working with attributes dynamically.

class Student:
    def __init__(self, name):
        self.name = name
        self.course = "Python"

student = Student("Meera")

print(getattr(student, "name"))
print(hasattr(student, "course"))

setattr(student, "course", "Data Analytics")
print(student.course)

delattr(student, "course")
print(hasattr(student, "course"))

Output:

Meera
True
Data Analytics
False

These functions are useful when attribute names are determined dynamically. For ordinary code, direct dot notation is often clearer.

12. The Difference Between a Class and an Object

A class describes a type of object, while an object is a specific instance of that type.

Class Object
Defines attributes and methods Uses the attributes and methods provided by its class
Acts as a template for instances Is an individual instance created from a class
Defined using the class keyword Created by calling the class
Example: class Student: Example: student1 = Student()
Can define common behavior Can store instance-specific state

Example

class Laptop:
    def __init__(self, brand, ram):
        self.brand = brand
        self.ram = ram

laptop1 = Laptop("Dell", "8GB")
laptop2 = Laptop("HP", "16GB")

print(laptop1.brand, laptop1.ram)
print(laptop2.brand, laptop2.ram)

Output:

Dell 8GB
HP 16GB

Laptop is the class. laptop1 and laptop2 are separate objects with different attribute values.

13. The __str__() Method

The special method __str__() defines a human-readable string representation of an object. It is used by str() and by print() when displaying the object.

class Student:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __str__(self):
        return f"{self.name}, age {self.age}"

student = Student("Aarav", 20)

print(student)

Output:

Aarav, age 20

Without a custom __str__() method, printing a custom object commonly produces a default representation that is less informative to users.

14. Understanding the __repr__() Method

The __repr__() method defines an object representation intended primarily for debugging and development. A good representation often makes it clear what kind of object is being displayed and which values it contains.

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __repr__(self):
        return f"Product(name={self.name!r}, price={self.price!r})"

product = Product("Keyboard", 1200)

print(repr(product))

Output:

Product(name='Keyboard', price=1200)

When a class defines __repr__() but not __str__(), the representation can also be used by str() as a fallback.

15. Understanding Object Identity and Type

Python provides built-in functions to inspect an object's type and identity.

  • type(object) returns the object's type.
  • isinstance(object, ClassName) checks whether an object is an instance of a class or one of its subclasses.
  • id(object) returns an identity value that is unique and constant for that object's lifetime.
  • is checks whether two references point to the same object.
class Student:
    pass

student1 = Student()
student2 = Student()
student3 = student1

print(type(student1))
print(isinstance(student1, Student))
print(student1 is student2)
print(student1 is student3)

Output:

<class '__main__.Student'>
True
False
True

student1 and student2 refer to separate objects. The variable student3 refers to the same object as student1.

16. Understanding Object References and Aliasing

In Python, variables refer to objects rather than acting as named containers that always hold independent copies. Assigning one object reference to another variable creates an additional reference to the same object.

class Student:
    def __init__(self, name):
        self.name = name

student1 = Student("Riya")
student2 = student1

student2.name = "Aman"

print(student1.name)
print(student2.name)

Output:

Aman
Aman

Both variables refer to the same object. Therefore, changing its name attribute through student2 is visible through student1 as well.

If you need an independent object, create a new instance or use an appropriate copying strategy.

17. Encapsulation: Keeping Data and Behavior Together

Encapsulation is an object-oriented programming principle that groups data and the methods that operate on it. It also encourages controlling how an object's internal state is accessed and changed.

Python does not enforce traditional private access in exactly the same way as some other languages. By convention, an attribute beginning with a single underscore, such as _balance, is treated as non-public. Double-leading-underscore names trigger name mangling, which helps avoid accidental name conflicts but does not provide absolute security.

class BankAccount:
    def __init__(self, holder, balance=0):
        self.holder = holder
        self._balance = balance

    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
        else:
            print("Deposit must be positive")

    def get_balance(self):
        return self._balance

account = BankAccount("Riya", 1000)
account.deposit(500)

print(account.get_balance())

Output:

1500

The deposit method validates the amount before changing the balance. This design encourages callers to use the provided behavior rather than update the balance arbitrarily.

18. Class Methods and Static Methods

Python supports instance methods, class methods, and static methods. Each has a different purpose.

18.1 Instance Method

An instance method receives the current object as its first argument, conventionally named self. It is useful when the method needs to read or modify instance attributes.

18.2 Class Method

A class method uses the @classmethod decorator and receives the class as its first argument, conventionally named cls. It is useful for working with class-level data or providing alternative ways to construct objects.

18.3 Static Method

A static method uses the @staticmethod decorator. It does not automatically receive an instance or class argument. It is useful for a related utility function that belongs conceptually to the class.

class Employee:
    company = "ABC Technologies"

    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def display(self):
        return f"{self.name}: {self.salary}"

    @classmethod
    def company_name(cls):
        return cls.company

    @staticmethod
    def is_valid_salary(salary):
        return salary >= 0

employee = Employee("Aman", 45000)

print(employee.display())
print(Employee.company_name())
print(Employee.is_valid_salary(45000))

Output:

Aman: 45000
ABC Technologies
True
Method Type First Automatic Argument Typical Purpose
Instance method self Operate on an individual object
Class method cls Work with class-level information or alternate constructors
Static method None Provide a related utility operation

19. Introduction to Inheritance

Inheritance allows a new class to reuse or extend behavior defined by another class. The existing class is called the base or parent class, and the new class is called the derived or child class.

class Animal:
    def eat(self):
        print("This animal eats food")

class Dog(Animal):
    def bark(self):
        print("The dog barks")

dog = Dog()

dog.eat()
dog.bark()

Output:

This animal eats food
The dog barks

The Dog class inherits the eat() method from Animal and defines its own bark() method.

20. Method Overriding

Method overriding occurs when a child class defines a method with the same name as a method inherited from its parent class. The child's implementation is used when that method is called on a child object.

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

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

animal = Animal()
dog = Dog()

animal.sound()
dog.sound()

Output:

Animal makes a sound
Dog barks

Overriding lets related classes provide specialized behavior while keeping a common method interface.

21. Using super() in Inheritance

The built-in super() function allows a class to call an implementation from the next class in the method resolution order. It is often used to extend initialization or behavior inherited from a parent class.

class Person:
    def __init__(self, name):
        self.name = name

class Student(Person):
    def __init__(self, name, course):
        super().__init__(name)
        self.course = course

    def display(self):
        print(self.name, self.course)

student = Student("Meera", "Python")
student.display()

Output:

Meera Python

The call to super().__init__(name) runs the parent initialization logic, after which the child class sets its own course attribute.

22. Abstraction and Polymorphism: A Brief Overview

Classes and objects provide a foundation for other object-oriented programming principles.

22.1 Abstraction

Abstraction focuses on exposing essential operations while hiding unnecessary implementation details. Python supports abstract base classes through the abc module.

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

class Square(Shape):
    def __init__(self, side):
        self.side = side

    def area(self):
        return self.side ** 2

square = Square(4)
print(square.area())

Output:

16

An abstract class with an unimplemented abstract method cannot normally be instantiated until a concrete subclass implements the required method.

22.2 Polymorphism

Polymorphism allows different object types to respond to the same method call in their own way.

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

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

animals = [Cat(), Dog()]

for animal in animals:
    print(animal.sound())

Output:

Meow
Bark

Both objects provide a sound() method, but each returns a different result.

23. Common Errors When Working with Classes and Objects

Error Cause Solution
NameError Using an undefined class or variable name Check spelling, scope, and definition order
AttributeError Accessing an attribute that does not exist Check initialization and attribute spelling
TypeError during object creation Providing the wrong number of constructor arguments Match the arguments expected by __init__()
Missing self Defining an instance method without the expected instance parameter Include self as the first parameter of instance methods
Unexpected shared data Using a mutable class attribute for per-object data Initialize separate mutable values in __init__()
Unexpected change across variables Two variables refer to the same object Create a new object or use an appropriate copy operation
Method returns None A method prints a value but does not return one Use return when the caller needs a result

24. Best Practices for Writing Classes

  • Use class names written in CapWords style, such as StudentRecord or BankAccount.
  • Use clear, descriptive names for attributes and methods.
  • Use __init__() to initialize the object's required state.
  • Use self consistently to access instance attributes and methods.
  • Keep each class focused on a clear responsibility.
  • Use instance attributes for data that should be independent for each object.
  • Use class attributes for genuinely shared data, especially immutable configuration values.
  • Validate important input before modifying object state.
  • Use __str__() and __repr__() when useful representations improve debugging or display.
  • Use inheritance when there is a genuine relationship between types, not merely to share unrelated code.
  • Prefer composition when one object should contain or use another object without being a specialized version of it.
  • Write small methods that are easy to test and maintain.

25. Mini Project: Student Management System

Project objective: Build a simple student management program using a class, instance attributes, methods, and multiple objects.

class Student:
    school = "Bright Future Academy"

    def __init__(self, name, roll_number, marks):
        self.name = name
        self.roll_number = roll_number
        self.marks = marks

    def calculate_grade(self):
        if self.marks >= 90:
            return "A"
        elif self.marks >= 75:
            return "B"
        elif self.marks >= 60:
            return "C"
        elif self.marks >= 40:
            return "D"
        else:
            return "F"

    def display_details(self):
        print("School:", self.school)
        print("Name:", self.name)
        print("Roll Number:", self.roll_number)
        print("Marks:", self.marks)
        print("Grade:", self.calculate_grade())
        print("-" * 25)


student1 = Student("Aarav", 101, 92)
student2 = Student("Meera", 102, 78)
student3 = Student("Karan", 103, 55)

student1.display_details()
student2.display_details()
student3.display_details()

Output:

School: Bright Future Academy
Name: Aarav
Roll Number: 101
Marks: 92
Grade: A
-------------------------
School: Bright Future Academy
Name: Meera
Roll Number: 102
Marks: 78
Grade: B
-------------------------
School: Bright Future Academy
Name: Karan
Roll Number: 103
Marks: 55
Grade: D
-------------------------

Project Explanation

  1. The Student class defines the structure and behavior of student objects.
  2. The class attribute school stores a school name shared by all students.
  3. The __init__() method initializes each student's name, roll number, and marks.
  4. The calculate_grade() method determines a grade based on marks.
  5. The display_details() method prints the student's information and calculated grade.
  6. Three independent student objects are created with different data.

This project demonstrates how classes help organize related information and behavior in a reusable structure.

26. Practice Exercises

  1. Create a Book class with attributes for title, author, and price. Create two objects and display their details.
  2. Create a Rectangle class with length and width attributes. Add methods to calculate area and perimeter.
  3. Create an Employee class with name, ID, and salary. Add a method that displays employee details.
  4. Create a BankAccount class with deposit and withdrawal methods. Prevent withdrawals that exceed the available balance.
  5. Create a Mobile class with brand, model, and price. Add a method to apply a discount.
  6. Demonstrate the difference between class attributes and instance attributes.
  7. Create a class with a custom __str__() method.
  8. Build a parent class named Vehicle and a child class named Bike.
  9. Demonstrate method overriding with two related classes.
  10. Create a student management program that calculates grades for multiple students.

27. Interview Questions and Answers

Q1. What is a class in Python?

A class is a user-defined type that groups related attributes and methods. It serves as a template for creating instances.

Q2. What is an object?

An object is an instance of a class. It can hold state through attributes and provide behavior through methods.

Q3. What is the difference between a class and an object?

A class defines the structure and behavior, while an object is an individual instance created from that class.

Q4. What is __init__()?

__init__() is an initialization method that runs after a new instance is created. It is commonly used to assign initial attribute values.

Q5. What is self in Python?

self conventionally refers to the current instance inside an instance method. Python supplies it automatically when the method is called through an object.

Q6. What are instance attributes?

Instance attributes belong to individual objects. They are commonly initialized using assignments such as self.name = name.

Q7. What are class attributes?

Class attributes are defined in the class body and are available through the class and its instances unless shadowed by an instance attribute.

Q8. What is an instance method?

An instance method is a function defined inside a class that normally receives the current object as its first parameter, conventionally named self.

Q9. What is inheritance?

Inheritance allows a child class to reuse or extend attributes and methods from a parent class.

Q10. What is method overriding?

Method overriding occurs when a child class defines a method with the same name as an inherited method, providing its own implementation.

Q11. What is the purpose of super()?

super() provides access to implementations in the next class in the method resolution order. It is commonly used to extend inherited initialization or behavior.

Q12. What is encapsulation?

Encapsulation groups data and related methods together and encourages controlled access to an object's internal state.

Q13. What is the difference between __str__() and __repr__()?

__str__() aims to provide a readable representation for users. __repr__() aims to provide a useful representation for debugging and development.

Q14. What is a class method?

A class method is defined with @classmethod and receives the class as its first argument, conventionally named cls.

Q15. What is a static method?

A static method is defined with @staticmethod and does not automatically receive the instance or class as an argument.

28. Quick Revision Checklist

  • A class defines a type and serves as a template for instances.
  • An object is an instance of a class.
  • Use the class keyword to define a class.
  • Call the class name to create an object.
  • Use __init__() to initialize instance attributes.
  • self refers to the current instance in instance methods.
  • Instance attributes store object-specific data.
  • Class attributes provide shared values through the class.
  • Methods define the behavior associated with a class.
  • __str__() and __repr__() provide object representations.
  • isinstance() checks whether an object is an instance of a given type.
  • Inheritance supports reuse and specialization.
  • Encapsulation organizes data and behavior and encourages controlled state changes.
  • Class methods use cls, while static methods do not receive an automatic instance or class argument.

29. Conclusion

Classes and objects are essential building blocks of object-oriented programming in Python. Classes define the attributes and methods that describe a type, while objects represent individual instances with their own state.

By learning to create classes, initialize objects with __init__(), use self, define methods, and distinguish class attributes from instance attributes, you can build programs that are organized, reusable, and easier to maintain. Concepts such as inheritance, encapsulation, abstraction, and polymorphism extend these foundations for larger applications.

Continue your learning with the Python Training Course, register through the JustAcademy Course Demo page, or visit the JustAcademy website for additional learning resources.

whatsapp