Technology  /  Python

🐍 Python 78 guides · updated 2026

From first variable to OOP, generators, and real projects β€” the language that runs everything from data pipelines to AI agents, taught the practical way.

Python Classes and Objects: Blueprints, Instances, and How They Relate

Before writing a single line of OOP code, it helps to understand the relationship between a class and an object. The analogy that works best: a class is an architectural blueprint, and an object is the actual building constructed from it. You can build many buildings from the same blueprint, each with its own address, colour, and layout β€” all sharing the same fundamental structure.

Defining a Class

In Python, you define a class with the class keyword followed by a name (capitalised by convention) and a colon. Everything indented below belongs to the class.

class Book:
"""Represents a book in a library system."""
pass # empty for now

This is a valid class. It does nothing useful yet, but Python accepts it. The pass statement is a placeholder that tells Python β€œno code here.”

Creating an Object

You create an object β€” also called an instance β€” by calling the class like a function.

book1 = Book()
book2 = Book()
print(type(book1)) # <class '__main__.Book'>
print(book1 is book2) # False β€” two separate objects

book1 and book2 are distinct objects. They both follow the Book blueprint, but they occupy different spots in memory and can hold different data.

The __init__ Method

A bare class without any initialisation is rarely useful. The __init__ method runs automatically every time an object is created. Use it to set up the object’s initial state.

class Book:
def __init__(self, title, author, pages):
self.title = title
self.author = author
self.pages = pages
book1 = Book("Sapiens", "Yuval Noah Harari", 443)
book2 = Book("The Pragmatic Programmer", "David Thomas", 352)
print(book1.title) # Sapiens
print(book2.author) # David Thomas

Each object stores its own title, author, and pages. The data is separate per instance even though both objects were created from the same Book class.

Understanding self

The self parameter is how Python passes the current object into its own methods. When you write book1.title, Python is accessing the title attribute that belongs to the book1 object specifically.

class Counter:
def __init__(self):
self.count = 0 # each Counter object starts at zero
def increment(self):
self.count += 1 # modifies THIS object's count
def reset(self):
self.count = 0
c1 = Counter()
c2 = Counter()
c1.increment()
c1.increment()
c2.increment()
print(c1.count) # 2
print(c2.count) # 1 β€” completely independent

self is not a keyword in the same way class is β€” you could name it anything β€” but the convention is universal and you should stick with it.

Instance Methods

Methods are functions defined inside a class. An instance method receives self as its first argument, giving it access to the object’s data.

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)
def is_square(self):
return self.width == self.height
r = Rectangle(5, 3)
print(r.area()) # 15
print(r.perimeter()) # 16
print(r.is_square()) # False
sq = Rectangle(4, 4)
print(sq.is_square()) # True

Methods keep related behaviour close to the data it operates on. The area() calculation belongs with the Rectangle class, not scattered through your codebase.

Inspecting Objects with __dict__

Every instance stores its attributes in a dictionary you can access directly.

class Product:
def __init__(self, name, price, sku):
self.name = name
self.price = price
self.sku = sku
p = Product("Keyboard", 79.99, "KB-001")
print(p.__dict__)
# {'name': 'Keyboard', 'price': 79.99, 'sku': 'KB-001'}

This is useful for debugging. It shows you exactly what data an object holds at any point in time.

Class Variables vs Instance Variables

Not all data belongs to individual objects. Some data is shared across all instances β€” that’s what class variables are for.

class Employee:
company = "Acme Corp" # class variable β€” shared by all instances
def __init__(self, name, department):
self.name = name # instance variable β€” unique per object
self.department = department # instance variable
emp1 = Employee("Alice", "Engineering")
emp2 = Employee("Bob", "Marketing")
print(emp1.company) # Acme Corp
print(emp2.company) # Acme Corp
print(emp1.name) # Alice
print(emp2.name) # Bob

Change Employee.company and all existing instances see the new value. Change emp1.name and only emp1 is affected.

One important caveat: if you assign to emp1.company = "New Corp", Python creates a new instance variable on emp1 rather than modifying the class variable. Always use Employee.company = "..." to update a shared class variable.

Common Mistakes

Forgetting self in method definitions. Every instance method must accept self as its first parameter, even if you do not use it inside the method body.

# Wrong β€” will raise TypeError when called
class Bad:
def greet():
print("Hello")
# Correct
class Good:
def greet(self):
print("Hello")

Confusing class variables with instance variables. Using a mutable class variable (like a list) as shared state can cause surprising bugs when one instance modifies it and all others see the change.

class Buggy:
items = [] # shared across ALL instances
def add(self, item):
self.items.append(item) # modifies the class-level list!
a = Buggy()
b = Buggy()
a.add("apple")
print(b.items) # ['apple'] β€” b sees it too, probably not what you wanted

Fix: put mutable defaults in __init__:

class Fixed:
def __init__(self):
self.items = [] # each instance gets its own list

Summary


Previous > Object-Oriented Programming        Next > The __init__() Constructor