We're actively developing the Python course; advanced AI courses will be released soon.

Learn AI Way

Python Classes and Objects


A Complete OOP Guide for Beginners (with a Production Mindset).

1. Introduction – Why Python Classes and Objects Matter

I have seen that most students (around the world) start their Python journey by writing small scripts.

In a script, code is centered around the following things:

They read data from files

Write code to loop over data

Finally, print the results and move on

But in my opinion, this is fine for learning Python language syntax. Based on my practical experience, real-world Python applications don’t scale with scripts alone.

Let’s first understand the problem with script-based Python code to have better clarity and understanding.

The Problem with Script-Based Python Code

Suppose we are managing students’ information as shown below:

names = ["Rahul", "Amit"]
ages = [20, 25]
marks = [70, 90]

The above written code is good at the beginning.

Once you start adding more names, updating marks, or passing data between functions, this code will become fragile.

Lists can go out of sync easily and silently. Bugs will start appearing. Overall, it would be difficult to manage the code.

In fact, this is a very common problem in beginner Python programs without classes and objects.

Why Python Uses Classes and Objects

In real software projects, developers don’t think in lists. They think in entities.

For example, a Student is a single unit with:

data – name, age, marks
behavior – update_marks(), display_details()

The above written approach is called Object-Oriented Programming (OOP).

Developers can model real-world problems naturally and safely with Python classes and objects.

Why This Matters for Real Python Developers

Once you understand Python classes and objects:

It becomes easier to read and extend your code
Your data and behavior stay together
Your programs look like real production code

That’s why Python classes and objects are considered a core Python skill that every student should learn to become a production developer.

In this guide, I will explain Python classes and objects in a simple way with real-world examples.

Prerequisite:
A basic understanding of Variables in Python, Python Functions Basics , and Python Lists - Basics will help you understand this Python classes and objects for beginners guide more effectively.

2. What Is Object-Oriented Programming (OOP) in Python?


It is a programming approach where data and operations (also called methods) are grouped together.

This is a key shift from script-based Python programming to real-world Python development.

In object-oriented programming, each real-world concept is treated as a unit that contains two things:

data (attributes)
behavior (methods)

To be more precise, Python is an object-oriented language because almost everything in Python is an object.

For example, numbers, strings, lists, and even functions have methods and properties.

If you want to write clean and scalable code, then you should have a good understanding of Python OOP concepts like classes and objects.

3. What Is a Class? (With Example)


A class in Python is a blueprint that contains two things:

Data (also called attributes)
Methods (also known as behavior)

Let’s understand each in a simple way.

Data

These are the variable names inside a class that store information about an entity.
Examples are name, age, and marks.

Methods

These are functions inside a class, and each function defines what actions can be performed using the data.

Example: Student Class in Python

class Student:
    name = "Amit"
    marks = 80

    def display(self):
        print("Name:", self.name)
        print("Marks:", self.marks)

Explanation

In the above program, we have created a Student class that acts as a blueprint.

This class contains data and a function (method).

Next, we have defined two attributes: name and marks that hold information.

After that, we have defined a method display() that prints these two attributes.

Here, self represents the current object created from the class.

It allows a method to access data and print the data of that same object.

Please note that a class can have multiple attributes and methods as well.

4. What Is an Object in Python? (Simple Explanation for Beginners)

An object is the real instance created from a class.

Example

class Student:
    name = "Amit"
    marks = 80

    def display(self):
        print("Name:", self.name)
        print("Marks:", self.marks)

s1 = Student()
s1.display()

Explanation

In the above example, Student is a class (blueprint).

The below line of code creates a real object from the Student class:

s1 = Student()

Please note that s1 is an object that contains:

data (name and marks)

behavior (display)

In the last line, s1.display() is used to call the display method using the s1 object for that specific object.

If you want, you can create multiple objects from the same class like:

s1 = Student()

s2 = Student()

Please note that here s1 and s2 objects are created from the same Student class, but s1 and s2 are different objects.

Each object can hold its own data and methods differently.

5. What Is __init__() Method in Python?


It is also called a constructor in object-oriented programming.

In simple words, it is used to initialize data when a new object is created from a class.

The __init__() method ensures that every object starts with the required data already set (instead of being empty).

This helps to avoid bugs caused by missing values, and this is how code is written in real-world applications.

Example: __init__() Method in Python Class

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

s1 = Student("Amit", 25)

Explanation

In the above program, first the s1 object is created with the below line of code:

s1 = Student("Amit", 25)

Then, the __init__() method is called automatically.

The __init__() method receives values (Amit and 25), and these values are assigned to the object’s data.

In this way, Python ensures that the object is created in a valid and usable state (with required data).

6. Why Use self in Python Classes? (Beginner Explanation)

In Python language, self refers to the current object created from a class.

It is also used to store and access data inside that specific object.

Example:

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

s1 = Student("Rocky", 22)

Explanation:

In the above program, when we write the following statement:

self.name = name

The name on the right-hand side (RHS) is the value passed to the function as a parameter.

This value exists only inside the function (constructor), and self.name on the left-hand side (LHS) is an attribute of the object.

In simple words, the value received (Rocky), which is passed as a function parameter (name), is assigned to an object attribute (self.name).

Note: At runtime, the attribute name is created automatically inside the object when the following statement is executed:

self.name = name

In Python, you don’t need to declare attributes in advance.

Call-flow Illustration:

When Python runs the following line:

s1 = Student("Rocky", 22)

Internally, Python does the following:

Student.__init__(s1, "Rocky", 22)

Explanation:

Object s1 is automatically passed as self.

In simple words, self contains the s1 object reference.

This is how self connects methods to an object, and it also prevents data from mixing between multiple objects.

Now, you have clarity about self in Python.

7. Default Values in __init__()

In real projects, sometimes we don’t have every piece of information available, or we don’t provide all the values to the __init__() method.

Some values are optional, and Python allows this using default values in __init__().

Example:

class User:
    def __init__(self, name, role="guest"):
        self.name = name
        self.role = role

u1 = User("Admin")
u2 = User("Amit", "manager")

Explanation:

In the above example, we have set a default value using role="guest".

When we create the u1 object and don’t pass any role, then Python uses the default value "guest" for role.

In the next line, when we create the u2 object and pass role value as "manager", then the default value (guest) is overridden.

Why This Is Useful?

Default values inside the __init__() method give you these benefits:

Flexibility: Objects can be created even when partial information is available.

Readability: Objects are created in a clean way, and it becomes easy to understand code.

Safety: Objects are created with sensible values, so you never miss important data.

8. Multiple Parameters in __init__() (Normal in Real Projects)


In real projects, when you create objects, each object generally represents a complete entity, not just one or two values.

That is why it is very common to use multiple parameters in the __init__() method in production code.

Example: Python Class with Multiple Parameters

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

Explanation:

In the above code, this class represents a Product with all its important details (like id, name, price, stock, etc.).

Each parameter passed to the __init__() method becomes data stored inside the object.

In real applications, these multiple values can come from multiple sources like databases, APIs, or user input.

When you pass multiple parameters, it ensures that the object is created with complete data.

9. Instance Variables vs Class Variables in Python


In Python classes, you can define two types of variables.

Instance variables belong to each individual object, and class variables belong to the class itself and are shared by all objects.

It is very important to understand the difference between these two variables in Python, because it helps you write correct and bug-free object-oriented Python code.

Let us try to understand each by taking two different examples.

a) Instance Variables (Object-Specific)


These variables are created inside the __init__() method and belong to a specific object.

Example
:

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

Explanation:

In the above shown code, self.name is an instance variable.

When you create an object of the Student class, then each object gets its own copy of name.

If you change one object’s data, then it won’t affect others.

b) Class Variables (Shared by All Objects)


These variables are defined inside the class but outside the __init__() method.

Example:

class Student:
    school_name = "XYZ School"

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

Explanation:

In the above shown code, school_name is a class variable, and it is shared by all student objects.

Production Example: E-commerce Order System


Let me share a small example here that I have used in one of my real-world projects.

In this project, we used class variables for shared configuration and instance variables for request-specific data, which helped keep the system clean and bug-free.

Example
:

class Order:
    tax_rate = 0.18

    def __init__(self, order_id, amount):
        self.order_id = order_id
        self.amount = amount

    def total_amount(self):
        return self.amount + (self.amount * Order.tax_rate)

o1 = Order(201, 1000)
o2 = Order(202, 1500)

print(o1.total_amount())
print(o2.total_amount())

Output:

1180.0

1770.0

Explanation:

In the above program, tax_rate is a class variable because it is shared by all orders.

Also, self.order_id and self.amount are instance variables because each order stores its own order id and amount.

The total_amount() method calculates and returns the final amount (including tax) using the shared tax rate.

10. Methods in a Class

A method in a class is a function, and it defines what an object can do.

In simple words, it represents the behavior or actions of an object.

Example: Student Class with Multiple Methods

class Student:
    def display(self):
        print("Student details")

    def is_passed(self):
        return True

s1 = Student()
s1.display()
print(s1.is_passed())

Output:

Student details

True

Explanation:

In the above program, we have defined two methods in the Student class.

The two method names are:

display()
is_passed()

After that, an object s1 is created from the Student class.

In the next two lines, both methods are called using the object s1, and outputs are printed.

Please note that a class can have multiple methods (based on need), and each method inside a class represents a different behavior.
Methods are always called using an object.

11. The __str__() Method (Readable Objects)


The __str__() method defines how an object is converted to text.

Always remember that the __str__() method is called automatically by Python when an object is printed or needs a readable string.

You don’t call it directly.

Example: __str__() in a Student class

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

    def __str__(self):
        return f"Student Name: {self.name}"

print(Student("Raj"))

Output:

Student Name: Raj

Explanation:

In the above program, firstly we create a class named Student, and inside the Student class, we define two methods:

__init__()
__str__()

After that, when we create a new object using Student("Raj"), the constructor __init__() is called automatically, and it initializes the variable name with the value "Raj".

After that, when Python tries to print the object using
print(Student("Raj")), it does not print the object memory address. Instead, Python automatically calls the __str__() method.

The __str__() method returns a readable string.

f"Student Name: {self.name}" is an f-string which allows the value of self.name to be directly inserted into the string, and finally, Python prints a readable output like:

Student Name: Raj

(instead of a memory address).

ALWAYS REMEMBER:

In Python language, when you write:

print(obj)

Python doesn’t directly print the object.
Behind the scenes, it does the following:

text = obj.__str__()
print(text)

12. Encapsulation (Controlled Access)


Encapsulation means hiding internal data and allowing access to it only through defined methods.

It helps in two ways:

It protects data from being changed directly.
It keeps the program safe.

Example:

class Student:
    def __init__(self, name, marks):
        self.name = name        # public variable
        self.__marks = marks   # private variable

    def get_marks(self):
        return self.__marks

s = Student("Andy", 75)

print(s.name)
print(s.__marks)

Output:

Andy

AttributeError

Explanation:

In the above example, both name and __marks are instance variables.
name is a public variable (by default) because it is written using self.name. Hence, it can be accessed directly from outside the class.

__marks is a private variable because it represents sensitive data and is defined with double underscore (__).

As a result, you cannot access the __marks variable directly outside the class, but you can access the public variable name without any restriction.

Important Point:

In the above example, encapsulation is achieved by hiding sensitive data (__marks) and exposing it only through class methods.

By doing this, data safety is ensured and you follow the best practices in Python object-oriented programming.

Public vs Private Members

Private – A private variable or method is accessed only inside the class and is protected from direct access from outside.
Public – A public variable or method can be accessed directly from outside the class without any restriction.

13. Production Example: Logger (Extensible Design)


In one of my production projects, we implemented a logger class, and it tracks user actions in a clean and extensible way.

The following code records the user actions along with the exact time they occur. It prints log messages in a structured way like when, who, and what happened.

Example:

from datetime import datetime

class UserLogger:
    def __init__(self, username):
        self.username = username

    def log(self, action):
        time = datetime.now().isoformat()
        print(f"{time} | {self.username} | {action}")

Explanation:

In the above code, firstly we have imported the datetime class from the datetime module so that the current date and time can be captured.

After that, we have defined a class UserLogger that handles logging user actions.

Next, we have initialized the logger with a specific user inside the constructor method.

After that, we have defined a method log() to log a user action. Inside the log() method, we are capturing the current time in a standard and readable format.

Finally, the log message is printed showing when, who, and what.

Important Points:

Normally, the log() method is called in the following scenarios:

After a user logs in or logs out

When a file is uploaded or downloaded

When an error or warning occurs

When an API request is received

And a production developer places the logging call inside business logic so that every important event gets recorded.

14. Object Lifecycle: Creation → Use → Cleanup


An object lifecycle describes three important things:

Creation – At this stage, an object is created in memory when you instantiate a class
(for example, when __init__() is called)
Usage – In this stage, the object performs its main tasks
Cleanup – At this stage, the object is no longer needed, and Python releases it from memory and cleanup actions (like __del__(), if defined) may occur

In real-world applications, I have seen that sometimes bugs appear in an application because objects are not cleaned up properly (not by bad logic).

Let me write a simple code for better understanding.

Example:

class FileTracker:
    def __init__(self, filename):
        self.filename = filename
        print("Opened:", filename)

    def process(self):
        print("Processing file")

    def __del__(self):
        print("Closed:", self.filename)
# object creation
tracker = FileTracker("data.txt")

# object usage
tracker.process()

# object deletion
del tracker

Output:

Opened: data.txt

Processing file

Closed: data.txt

Explanation:

In the above program, when FileTracker object is created, it runs the __init__() method and prints "Opened: data.txt".

After that, the process() method is called, then Python executes the body inside process() method and prints "Processing file".

Please note that the process() method represents using the object while it is active.

Finally, when del tracker is executed, Python removes the reference to the object, and if no reference remains, then Python invokes the __del__() method for cleanup tasks (during garbage collection) and prints "Closed: data.txt".

Normally, a __del__() method is used for the following cleanup tasks:

Closing a file

Releasing a network connection

If you want to free an external resource (not Python memory)

If you want to print a message (for learning perspective)

Golden Lines:

del only removes references of an object

__del__() method is used for cleanup tasks

Python garbage collector releases the object from memory

15. Deleting Objects in Python


In Python, if you want to remove the reference of an object (when it is no longer required), then you can use the del keyword.

Example
:

del obj

Important Points:

It only removes the reference, not the object

There is no immediate guarantee about destruction of the object

The object is cleaned up only when Python garbage collector decides it is safe

16.  Five Common Mistakes Beginners Make in Python OOP


In my professional career, I have seen freshers or junior developers do the following mistakes regularly. So, sharing below with you, that will help you all to write clean code.

1. Forgetting self

Most of beginners forget to use self inside methods. Without self keyword, Python does not know which object’s data the method should use, and this mistake leads to errors in application.

2. Confusing class with object

Many beginners (from non-CS background) think that class and object are same thing. But class is only a blueprint and object is the instance from that particular class.

3. Putting heavy logic inside __init__():

Many times, freshers write heavy logic inside the __init__() method, but in real-world Python code, the __init__() method is used only to initialize data.
Complex business logic should always be written in separate methods or factory functions.

4. Creating meaningless classes:
Most of the time, beginners only store data inside classes and do nothing else.
If there is almost no behavior, then a simple function may be a better solution.

5. Ignoring __str__():

Sometimes, every Java developer ignores the __str__() method, and in that case, unreadable memory addresses are printed.

But one should define the __str__() method because it helps to make objects readable and also improves debugging in Python applications.

If you avoid these common mistakes, then it would help you to write clean, readable, and production-ready Python object-oriented code.

17. Why This Matters for AI, ML, and Cloud Development:

If you want to work with AI, Machine Learning, and Cloud systems in real projects, then one should have good knowledge about Python classes and objects.

1. PyTorch models are classes:

In AI and deep learning-based projects, we write models as Python classes.
In that, you define the model structure once, and after that, you create objects from it to train and run predictions.
That is why it is essential to learn classes, objects, and methods in Python.

2. ML pipelines manage state using objects:

Machine learning pipelines need to store state such as:

model parameters
training progress
evaluation results

In Python, we use objects to keep this data together with the logic that operates on it.
This makes ML code reusable and easier to debug.

3. Cloud services use class-based SDKs:

Today, most cloud platforms expose their services using class-based Python SDKs.

If you don’t understand Python OOP concepts, then it becomes difficult to understand or do cloud automation.

My advice would be: if you want to work in AI, ML, or Cloud engineering, then you should have an understanding of the following topics:

Python classes
Objects and methods
Object state and lifecycle

18. Frequently Asked Questions (Python Classes and Objects)


1. What is the difference between a class and an object in Python?

Ans:

A class is a blueprint that defines properties and methods, while an object is the real instance created from that class.

2. Why is self required in Python classes?

Ans:

self represents the current object, and it allows Python to store and access instance variables correctly.

3. What is the purpose of the __init__() method in Python?

Ans:

It is a constructor that is used to initialize object data when a class instance is created.
It ensures that every object starts in a valid and predictable state.

4. What is the difference between __str__() and __repr__() in Python?

Ans:

__str__() provides a human-readable representation of an object, mainly for printing and logging.
__repr__() is intended for developer-focused, unambiguous output that can help with debugging.

5. How are Python classes and objects used in real-world applications?

Ans:

Python classes and objects are used to model real-world entities such as:
users
bank accounts
machine learning models
cloud services

They also help to organize data, manage state, and build scalable, maintainable systems.

19. Summary:


If you want to write clean, scalable, and production-ready code, then Python classes and objects are the foundation of object-oriented programming.

In this guide, you have learned the following topics so far:

how Python classes work internally
how objects are created using the __init__() method
why self is required
how object state is managed using instance and class variables
multiple OOP examples like logging, encapsulation, object lifecycle, and safe cleanup

If you master these Python OOP concepts, then it would help you not only as a beginner, but you would also be able to write simple, clean code for building real applications in backend systems, machine learning, and cloud environments.