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 nowThis 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 objectsbook1 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) # Sapiensprint(book2.author) # David ThomasEach 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) # 2print(c2.count) # 1 β completely independentself 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()) # 15print(r.perimeter()) # 16print(r.is_square()) # False
sq = Rectangle(4, 4)print(sq.is_square()) # TrueMethods 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 Corpprint(emp2.company) # Acme Corpprint(emp1.name) # Aliceprint(emp2.name) # BobChange 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 calledclass Bad: def greet(): print("Hello")
# Correctclass 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 wantedFix: put mutable defaults in __init__:
class Fixed: def __init__(self): self.items = [] # each instance gets its own listSummary
- A class defines the structure and behaviour shared by all objects of that type.
- An object (instance) is a specific realisation of that class with its own data.
__init__sets up the object when it is created.selfrefers to the object the method is called on.- Instance variables are unique per object; class variables are shared across all instances.
Previous > Object-Oriented Programming ββββββ Next > The __init__() Constructor