Python Classes/Objects
Learn how Python classes and objects work: define classes, use __init__, create instances, work with attributes and methods, and understand inheritance.
Python is an object-oriented programming (OOP) language, which means it organizes code around objects rather than functions alone. A class is the blueprint that describes what data an object holds and what it can do. An object is a specific instance created from that blueprint.
This chapter covers:
- What classes and objects are, and why they matter
- Defining a class with
__init__, instance attributes, and methods - Class attributes versus instance attributes
- Creating and using objects
- Modifying and deleting attributes
- Checking object types with
isinstance() - A first look at inheritance
For a deeper dive into inheritance, see Python Inheritance. For more advanced OOP patterns, see Python Abstract Base Classes.
What Is a Class?
Think of a class as a cookie-cutter and an object as the cookie. You define the shape once (the class), then stamp out as many cookies (objects) as you need. Each object shares the same structure but holds its own data.
class Dog:
pass # an empty class — valid but not very useful yetThe class keyword followed by a name and a colon creates a new class. By convention, class names use CapWords (also called PascalCase): MyClass, BankAccount, HttpRequest.
The __init__ Method
The __init__ method (short for initialise) runs automatically every time you create a new object. It sets up the object's initial state by assigning values to its instance attributes.
class Dog:
def __init__(self, name, age):
self.name = name # instance attribute
self.age = age # instance attributeThe first parameter is always self — it refers to the object being created. Python passes it automatically; you never supply it yourself.
Instance Attributes vs. Class Attributes
| Kind | Defined inside | Belongs to | Shared? |
|---|---|---|---|
| Instance attribute | __init__ (via self) | Each object | No — each object has its own copy |
| Class attribute | Class body (outside any method) | The class itself | Yes — all objects share one copy |
class Dog:
species = "Canis familiaris" # class attribute — shared by all Dogs
def __init__(self, name, age):
self.name = name # instance attribute
self.age = age # instance attribute
fido = Dog("Fido", 3)
bella = Dog("Bella", 5)
print(fido.species) # Canis familiaris
print(bella.species) # Canis familiaris (same class attribute)
print(fido.name) # Fido
print(bella.name) # Bella (different instance attributes)Use a class attribute when a value is the same for every object of that type (e.g., the species of all Dogs). Use instance attributes for data that varies per object.
Defining Methods
A method is a function defined inside a class. Like __init__, it always takes self as its first parameter so it can access the object's own attributes.
class Dog:
species = "Canis familiaris"
def __init__(self, name, age):
self.name = name
self.age = age
def bark(self):
return f"{self.name} says: Woof!"
def description(self):
return f"{self.name} is {self.age} years old."You call a method on an object using dot notation — Python automatically passes the object as self:
fido = Dog("Fido", 3)
print(fido.bark()) # Fido says: Woof!
print(fido.description()) # Fido is 3 years old.The __str__ Method
Python calls __str__ when you pass an object to print() or str(). Without it, you get an unhelpful address like <__main__.Dog object at 0x...>. Defining it makes debugging much easier.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"Dog(name={self.name!r}, age={self.age})"
fido = Dog("Fido", 3)
print(fido) # Dog(name='Fido', age=3)Creating and Using Objects
Creating an object is called instantiation. You call the class like a function, passing any arguments that __init__ expects (excluding self):
You can create as many objects as you need from the same class — each is independent:
class Rectangle:
def __init__(self, width, height=1):
self.width = width
self.height = height
def area(self):
return self.width * self.height
def perimeter(self):
return 2 * (self.width + self.height)
r1 = Rectangle(4, 3)
r2 = Rectangle(5) # height defaults to 1
print(r1.area()) # 12
print(r1.perimeter()) # 14
print(r2.area()) # 5Notice that height=1 provides a default value: if you omit the second argument, Python uses 1 automatically.
Modifying and Deleting Attributes
You can change or add attributes on an object after it has been created, and delete them with del:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 30)
print(p.age) # 30
p.age = 31 # modify an existing attribute
print(p.age) # 31
p.email = "[email protected]" # add a new attribute at runtime
print(p.email) # [email protected]
del p.email # delete the attribute
# print(p.email) # would raise AttributeErrorWhile Python allows adding attributes freely, it is cleaner to declare all attributes inside __init__ so the class's structure is obvious at a glance.
Checking Object Types
Use isinstance() to check whether an object is an instance of a particular class. It returns True for the class itself and any of its parent classes:
class Animal:
pass
class Dog(Animal):
pass
rex = Dog()
print(isinstance(rex, Dog)) # True
print(isinstance(rex, Animal)) # True — Dog is a subclass of Animal
print(type(rex) is Dog) # True
print(type(rex) is Animal) # False — type() does not climb the hierarchyPrefer isinstance() over type() is in most code because it correctly handles subclasses.
A First Look at Inheritance
Inheritance lets a new class reuse and extend the behaviour of an existing one. The new class (child or subclass) automatically gains all the attributes and methods of the parent:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return f"{self.name} makes a sound."
class Dog(Animal):
def speak(self): # override the parent method
return f"{self.name} says: Woof!"
class Cat(Animal):
def speak(self):
return f"{self.name} says: Meow!"
dog = Dog("Rex")
cat = Cat("Whiskers")
print(dog.speak()) # Rex says: Woof!
print(cat.speak()) # Whiskers says: Meow!
print(isinstance(dog, Animal)) # TrueBoth Dog and Cat inherit __init__ from Animal, so they don't need to repeat it. They only override speak() to provide breed-specific behaviour.
For a full treatment of inheritance — including super(), multiple inheritance, and method resolution order — see Python Inheritance.
When Should You Use a Class?
Classes make most sense when:
- You have data and behaviour that belong together (a bank account that knows how to deposit and withdraw).
- You need multiple independent objects of the same type (many
Dogobjects, each with different names and ages). - You want to model real-world entities with a clear identity.
Simple utility functions that just transform input to output are often better as plain functions. Python does not require everything to be in a class.
Summary
| Concept | What it does |
|---|---|
class | Defines a new type |
__init__ | Initialises instance attributes when an object is created |
self | Refers to the current object inside a method |
| Instance attribute | Data belonging to one specific object |
| Class attribute | Data shared across all objects of the class |
| Method | A function defined inside a class; always receives self |
__str__ | Controls how print() displays the object |
isinstance() | Checks whether an object is an instance of a class or its subclasses |
| Inheritance | Lets a child class reuse and extend a parent class |