Back to Blog
Python

Python Classes and Objects: A Beginner to Pro Guide

9/15/2025
5 min read
 Python Classes and Objects: A Beginner to Pro Guide

Master Python OOP! This in-depth guide explains Classes, Objects, Inheritance & more with clear examples and real-world analogies. Perfect for beginners.

 Python Classes and Objects: A Beginner to Pro Guide

Python Classes and Objects: A Beginner to Pro Guide

Python Classes and Objects: From Beginner to Pro - The Ultimate Guide

Welcome, future coders! If you've been dabbling in Python, you've probably used things like lists, dictionaries, and strings. Did you know that these are all objects? This isn't just a fancy term; it's the foundation of a powerful programming paradigm called Object-Oriented Programming (OOP).

Understanding classes and objects is the single biggest leap from writing simple scripts to building complex, professional-grade software. It can feel abstract at first, but stick with me. By the end of this guide, you'll not only understand what they are but you'll also think in objects, which is a game-changer for any developer.

This comprehensive guide is designed to take you from absolute beginner to confident user. We'll use real-world analogies, write plenty of code, and explore best practices that you'd learn in a professional setting. Speaking of which, if you're serious about transforming your hobby into a career, our Python Programming and Full Stack Development courses at CoderCrafter are built around these fundamental concepts. To learn professional software development courses such as Python Programming, Full Stack Development, and MERN Stack, visit and enroll today at codercrafter.in.

Let's stop dealing with isolated variables and functions and start building organized, scalable systems!

Part 1: The "Aha!" Moment: Blueprints and Houses

Before we write a single line of code, let's get the core idea straight.

Imagine you want to build a house. You don't just start laying bricks. First, you create a blueprint. This blueprint defines:

  • The structure: how many rooms, where the doors go, the layout.

  • The attributes: the color of the walls, the type of flooring, the height.

  • The capabilities: the door can open(), the light can switch_on().

This blueprint is your Class. It's a plan, a template. It's not a real house itself.

Now, when you use that blueprint to construct an actual, physical house at 123 Main Street, that physical house is an Object (also called an Instance). You can use the same blueprint to build another, slightly different house at 456 Oak Avenue. That's a second object.

Both houses are distinct objects with their own wall colors and doorbells, but they were both created from the same foundational blueprint (the Class).

In programming terms:

  • A Class is a blueprint for creating objects.

  • An Object is a specific instance of a class, with actual data and functionality.

Part 2: Defining Your First Class in Python

Enough theory, let's code! We'll stick with our analogy.

python

# Defining a Class (The Blueprint)
class House:
    # The initializer (a special method called when an object is created)
    def __init__(self, address, wall_color="white"):
        # Attributes (Data)
        self.address = address
        self.wall_color = wall_color
        self.is_door_open = False  # Setting a default value

    # Methods (Functions inside a class that define behavior)
    def open_door(self):
        self.is_door_open = True
        print(f"The door at {self.address} is now open!")

    def close_door(self):
        self.is_door_open = False
        print(f"The door at {self.address} is now closed.")

    def paint_house(self, new_color):
        self.wall_color = new_color
        print(f"The house at {self.address} is now painted {new_color}.")

Let's break this down line by line:

  1. class House:: This defines a new class named House. By convention, class names in Python use CamelCase.

  2. def __init__(self, address, wall_color):: This is the initializer method (often mistakenly called the constructor). It's automatically called when you create a new House object.

    • self: This is the most crucial parameter. It's a reference to the current instance of the class. Through self, you can access the attributes and methods of that specific object. Think of self as the specific house you're currently talking about.

    • address, wall_color: These are parameters you pass in when creating the object.

  3. self.address = address: Here, we take the address parameter passed in and assign it to an attribute of the object called self.address. Attributes are variables that belong to the object.

  4. The methods open_door(), close_door(), and paint_house() define what a House object can do. Notice they all take self as their first parameter to act on the specific object.

Part 3: Creating Objects (Instantiation)

Now, let's use our blueprint to build some actual houses (create objects).

python

# Creating Objects (Instances of the House class)
house_1 = House("123 Main Street", "blue")
house_2 = House("456 Oak Avenue") # Uses the default "white" wall color

# Let's interact with our objects!
print(house_1.address)  # Output: 123 Main Street
print(house_2.wall_color) # Output: white

house_1.open_door()     # Output: The door at 123 Main Street is now open!
house_2.paint_house("red") # Output: The house at 456 Oak Avenue is now painted red.

# Checking the state of our objects
print(f"Is house_1's door open? {house_1.is_door_open}") # Output: True
print(f"What is house_2's new color? {house_2.wall_color}") # Output: red

See? house_1 and house_2 are completely independent objects. Opening the door of house_1 does not affect the door of house_2. They manage their own state.

Part 4: The Four Pillars of OOP in Python

OOP stands on four key principles. Our House example already uses them, but let's make them explicit.

1. Encapsulation

This is the bundling of data (attributes) and methods that operate on that data into a single unit (the class). It also involves restricting direct access to some of an object's components, which is often called information hiding.

In our House class, the attributes wall_color and is_door_open are public by default (meaning you can access them directly with house_1.wall_color). But we can make them private.

python

class SecureHouse:
    def __init__(self, address, password):
        self.address = address # Public attribute
        self.__password = password # Private attribute (prefixed with double underscores)
        self.__alarm_armed = True # Private attribute

    def disarm_alarm(self, entered_password):
        if entered_password == self.__password:
            self.__alarm_armed = False
            print("Alarm disarmed!")
        else:
            print("Wrong password! Alarm is still armed.")

    # A public method to safely access a private attribute's value
    def is_alarm_armed(self):
        return self.__alarm_armed

my_secure_house = SecureHouse("789 Hillside", "secret123")
# print(my_secure_house.__password) # This would cause an AttributeError
my_secure_house.disarm_alarm("guess")    # Output: Wrong password!
my_secure_house.disarm_alarm("secret123") # Output: Alarm disarmed!
print(my_secure_house.is_alarm_armed())   # Output: False

Encapsulation helps prevent the accidental modification of data and enforces control through well-defined methods.

2. Inheritance

This allows us to define a class that inherits all the attributes and methods from another class. The new class is called a child class (or derived class), and the class it inherits from is the parent class (or base class). It promotes code reusability.

Let's create a more specific type of House.

python

# Parent Class
class House:
    def __init__(self, address, bedrooms):
        self.address = address
        self.bedrooms = bedrooms

    def describe(self):
        print(f"This house at {self.address} has {self.bedrooms} bedrooms.")

# Child Class
class Mansion(House): # Mansion inherits from House
    def __init__(self, address, bedrooms, butlers):
        super().__init__(address, bedrooms) # Call the parent's __init__ method
        self.butlers = butlers # New attribute specific to Mansion

    # New method specific to Mansion
    def call_butler(self, butler_name):
        print(f"{butler_name}, please bring the tea to the {self.bedrooms} bedroom mansion.")

    # We can also override the parent's method
    def describe(self):
        print(f"This magnificent mansion at {self.address} has {self.bedrooms} bedrooms and {self.butlers} butlers.")

# Using Inheritance
my_house = House("123 Main St", 2)
my_mansion = Mansion("1 Billionaire's Row", 10, 3)

my_house.describe()   # Output: This house at 123 Main St has 2 bedrooms.
my_mansion.describe() # Output: This magnificent mansion at 1 Billionaire's Row has 10 bedrooms and 3 butlers.
my_mansion.call_butler("Jeeves") # Output: Jeeves, please bring the tea to the 10 bedroom mansion.

Notice how Mansion got all the functionality of House for free and then added its own specializations.

3. Polymorphism

This means "many forms." It allows methods to do different things based on the object that is acting upon them. We already saw a glimpse of it: the describe() method behaved differently for a House object versus a Mansion object. This is called method overriding.

A more classic example is with animals:

python

class Dog:
    def speak(self):
        return "Woof!"

class Cat:
    def speak(self):
        return "Meow!"

def make_animal_speak(animal):
    print(animal.speak())

# Different objects, same method name, different results (forms)
make_animal_speak(Dog()) # Output: Woof!
make_animal_speak(Cat()) # Output: Meow!

The function make_animal_speak() doesn't need to know what type of animal it is. It just knows the animal can speak(). This makes code very flexible and modular.

4. Abstraction

Abstraction is the concept of hiding complex implementation details and showing only the essential features of an object. It's like knowing how to drive a car without needing to understand the mechanics of the internal combustion engine.

In Python, abstraction is achieved using Abstract Base Classes (ABCs) from the abc module, but it's a more advanced topic. The simple idea is that you define a method in a base class but don't implement it, forcing any child class to provide its own implementation.

Part 5: A Real-World Use Case: A Simple E-commerce System

Let's model something practical. Imagine building the backend for a small online store.

python

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

    def display_info(self):
        print(f"{self.name} (ID: {self.product_id}) - ${self.price:.2f}")

class ShoppingCart:
    def __init__(self):
        self.items = [] # A list to hold Product objects

    def add_item(self, product, quantity=1):
        for i in range(quantity):
            self.items.append(product)
        print(f"Added {quantity} x {product.name} to cart.")

    def remove_item(self, product):
        if product in self.items:
            self.items.remove(product)
            print(f"Removed 1 x {product.name} from cart.")
        else:
            print(f"{product.name} not found in cart.")

    def calculate_total(self):
        total = sum(item.price for item in self.items)
        return total

    def view_cart(self):
        if not self.items:
            print("Your cart is empty.")
        else:
            print("Your Shopping Cart:")
            for item in self.items:
                print(f"  - {item.name} (${item.price:.2f})")
            print(f"Total: ${self.calculate_total():.2f}")

# Let's simulate a user's session
# Create some products
laptop = Product("P100", "Super Laptop", 999.99)
mouse = Product("P101", "Gaming Mouse", 49.99)
headphones = Product("P102", "Wireless Headphones", 129.99)

# A customer arrives and gets a cart
my_cart = ShoppingCart()

# The customer shops
my_cart.add_item(laptop)
my_cart.add_item(mouse, 2)
my_cart.view_cart()
# Output:
# Your Shopping Cart:
#   - Super Laptop ($999.99)
#   - Gaming Mouse ($49.99)
#   - Gaming Mouse ($49.99)
# Total: $1149.97

my_cart.remove_item(mouse)
my_cart.view_cart()
# Output: (one mouse is removed)
# Total: $1099.98

This is a simplified model, but it shows the power of OOP. We've created logical entities (Product, ShoppingCart) that interact with each other, mirroring the real world. This code is organized, easy to reason about, and easy to extend (e.g., adding a Customer class next). Building such systems is a core part of our Full Stack Development program at CoderCrafter, where you learn to connect Python backends like this to modern frontend frameworks.

Part 6: Best Practices and Pythonic OOP

  1. Use CamelCase for Class Names: MyClass, DatabaseConnection.

  2. Use snake_case for methods and attributes: my_method, instance_variable.

  3. Keep __init__ Simple: It should only initialize the object's attributes. Don't put complex logic here.

  4. Use Meaningful Names: class Employee is better than class Data.

  5. Composition over Inheritance: Often, it's better to build objects by having them contain other objects (composition) rather than inheriting from them. For example, a Car class might have an Engine object as an attribute rather than inheriting from an Engine class. This creates more flexible code.

  6. Use @property for Getters and Setters: Instead of writing get_name() and set_name() methods, you can use the @property decorator to create elegant, attribute-like access.

    python

    class Person:
        def __init__(self, name):
            self._name = name # "Protected" attribute by convention
    
        @property
        def name(self):
            return self._name.title() # Format the name when getting it
    
        @name.setter
        def name(self, value):
            if isinstance(value, str) and len(value) > 0:
                self._name = value
            else:
                raise ValueError("Name must be a non-empty string.")
    
    p = Person("alice")
    print(p.name)    # Output: Alice (uses the getter)
    p.name = "bob"   # Uses the setter
    print(p.name)    # Output: Bob
    # p.name = ""    # Would raise ValueError

Part 7: Frequently Asked Questions (FAQs)

Q: What's the difference between a class and an object?
A: A class is the blueprint (e.g., the list class). An object is an instance created from that blueprint (e.g., my_list = [1, 2, 3]).

Q: What is self?
A: self is a reference to the current instance of the class. It's how an object accesses its own data and methods. It must be the first parameter of any instance method.

Q: When should I use a class versus a function?
A: Use functions for simple, isolated tasks. Use classes when you need to model something that has both data (attributes) and behavior (methods), or when you need to create multiple instances of the same concept.

Q: What is the difference between __init__ and a constructor?
A: Technically, in Python, __new__ is the constructor (which creates the new object), and __init__ is the initializer (which initializes the newly created object). For 99% of use cases, you only need to define __init__.

Q: Are private attributes truly private?
A: Not really. Python has a concept called "name mangling." self.__private_var becomes _MyClass__private_var behind the scenes. It's more of a strong warning to other programmers: "Don't touch this unless you know what you're doing!"

The journey from here involves diving deeper into design patterns, advanced OOP concepts, and building larger projects. If you're ready to take that next step with structured guidance, peer collaboration, and expert mentorship, we are here to help. To learn professional software development courses such as Python Programming, Full Stack Development, and MERN Stack, visit and enroll today at codercrafter.in. Let's build your future, one object at a time.


Related Articles