Skip to main content

Classes and Objects: Core OOP Concepts

Object-Oriented Programming (OOP) is a paradigm that solves the complexity problem of large programs by bundling data and the functions that operate on that data into a single unit called an object. This is a fundamental shift from procedural programming, where data and functions are separate. Classes are the blueprints; objects are the concrete instances created from those blueprints.

The Blueprint-and-Instance Model

Imagine you are an architect designing a house. You draw a detailed blueprint that specifies every essential property: room count, window dimensions, roof type, and foundation specifications. This blueprint isn't a house — it's the plan for one.

A class is like that blueprint. It defines the structure and behavior that all objects of a certain type will share. An object is like an actual house built from the blueprint. All houses built from the same blueprint share the same structure, but each is a unique, independent instance with its own state (one might be painted blue, another red).

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

A class is a template defining properties (attributes) and behaviors (methods). An object is a specific instance created from that class. Think of it this way:

  • Class: The recipe for a cake.
  • Object: An actual cake baked from that recipe.

You define a class once and create as many objects from it as you need. Each object maintains its own state independent of others, even though they all follow the same template.


Defining Your First Class

To define a class in Python, use the class keyword followed by the class name. By convention, class names use PascalCase (capitalized words, no underscores). Here's a minimal example:

class Dog:
pass # 'pass' is a placeholder meaning no content yet

This is a valid, empty class. It doesn't have any attributes or methods yet, but the class exists and you can create instances from it. An empty class is rarely useful in real programs, so let's move to the next step.


Initializing Objects with the init() Constructor

The __init__() method is Python's constructor. It's automatically invoked every time you create a new object from a class. Its job is to set up the initial state (attributes) of the object.

The first parameter of __init__() is always self, which refers to the specific instance being created. Use self to create and assign attributes:

How do you define a class with initial attributes using init()?

class Dog:
# This is the constructor method
def __init__(self, name: str, age: int):
# 'self' refers to the specific Dog object being created.
# We are creating attributes on the object and assigning them values.
self.name = name
self.age = age
print(f"A new dog named '{self.name}' has been created!")

Code breakdown:

  • def __init__(self, name, age): — The method signature. self is always first, followed by your custom parameters.
  • self.name = name — Creates an attribute called name on this specific object and assigns it the value of the name parameter.
  • self.age = age — Creates an attribute called age on this specific object.

These self.name and self.age are called instance attributes because they belong to a single instance (object) of the class, not to the class itself.


Creating Objects (Instantiation)

Instantiation is the process of creating an object from a class. You invoke the class like a function, passing the arguments that __init__() expects (Python automatically handles self):

How do you create objects from a class?

class Dog:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
print(f"A new dog named '{self.name}' has been created!")

# Instantiate two separate Dog objects
dog_one = Dog("Buddy", 4)
dog_two = Dog("Lucy", 2)

# Access each object's unique attributes using dot notation
print(f"{dog_one.name} is {dog_one.age} years old.")
print(f"{dog_two.name} is {dog_two.age} years old.")

Output:

A new dog named 'Buddy' has been created!
A new dog named 'Lucy' has been created!
Buddy is 4 years old.
Lucy is 2 years old.

Key observations:

  • Each call to Dog() creates a completely separate object.
  • dog_one and dog_two are independent — changing dog_one.name does not affect dog_two.name.
  • __init__() ran twice, once for each object creation.
  • You access attributes using dot notation: object.attribute.

Key Takeaways

  • OOP bundles data and behavior: Classes group related attributes and methods.
  • Classes are templates: They define the structure all instances will follow.
  • Objects are instances: Each object maintains its own independent state.
  • __init__() is the constructor: Use it to set up initial attributes when an object is created.
  • self refers to the instance: Always the first parameter in instance methods.
  • Instantiation creates objects: Call the class like a function to create a new instance.

Frequently Asked Questions

What happens if you don't define init() in a class?

Python provides a default constructor that does nothing. You can still create instances, but no attributes will be automatically assigned. If your class needs to initialize attributes, define __init__().

Can you create an object without any parameters?

Yes, if __init__() takes no parameters besides self, you can create objects with no arguments: my_obj = MyClass(). If __init__() requires parameters (like name and age in the Dog example), you must provide them, or Python raises a TypeError.

What is the difference between a class attribute and an instance attribute?

An instance attribute belongs to a single object and varies from object to object. An class attribute is shared by all instances of the class. In the Dog example, name and age are instance attributes. You define class attributes directly in the class body (outside __init__()): class Dog: species = "Canis familiaris" — all Dog instances share this value unless explicitly overridden.

How do you access attributes of an object?

Use dot notation: object.attribute_name. For example, dog_one.name retrieves the name attribute of the dog_one object. You can also modify attributes: dog_one.name = "NewName" changes it.

Can an object have attributes not defined in init()?

Yes. You can add attributes dynamically at any time: dog_one.color = "brown". However, for clarity and maintainability, define all expected attributes in __init__(). Dynamically adding attributes makes code harder to understand.


Further Reading