Why classes exist
Once a program tracks several related values — a bank account's owner, balance and history, say — passing them around as loose variables becomes messy. A class bundles data (attributes) and the functions that operate on it (methods) into one blueprint. Each concrete thing built from the blueprint is an object or instance. You have used objects all along: "abc".upper() calls a method on a string object.
Defining a class
class Account:
bank = "PyBank" # class attribute (shared)
def __init__(self, owner, balance=0):
self.owner = owner # instance attributes
self.balance = balance
self.history = []
def deposit(self, amount):
if amount <= 0:
raise ValueError("Deposit must be positive")
self.balance += amount
self.history.append(("deposit", amount))
def withdraw(self, amount):
if amount > self.balance:
raise ValueError("Insufficient funds")
self.balance -= amount
self.history.append(("withdraw", amount))
acc = Account("Ada", 100)
acc.deposit(50)
acc.withdraw(30)
print(acc.owner, acc.balance, acc.history)
print(Account.bank, acc.bank)__init__runs automatically when you create an instance. It is the constructor.selfis the instance the method was called on. Python passes it for you:acc.deposit(50)is reallyAccount.deposit(acc, 50).- Class attributes are shared by all instances; instance attributes belong to one object.
__str__ and __repr__
Printing an object gives an unhelpful <__main__.Account object at 0x…> unless you define how it should look:
class Point:
def __init__(self, x, y):
self.x, self.y = x, y
def __repr__(self): # for developers / debugging
return f"Point({self.x}, {self.y})"
def __str__(self): # for users / print()
return f"({self.x}, {self.y})"
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
p, q = Point(1, 2), Point(3, 4)
print(p) # uses __str__
print([p, q]) # lists use __repr__
print(p + q, p == Point(1, 2))These double-underscore ("dunder") methods let your objects work with print, ==, +, len(), sorting and more. The Coding Python app's "operator overloading" example goes further.
Inheritance and super()
A subclass inherits everything from its parent and can add or override behaviour:
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "..."
def intro(self):
return f"{self.name} says {self.speak()}"
class Dog(Animal):
def speak(self):
return "Woof"
class Puppy(Dog):
def __init__(self, name, age_weeks):
super().__init__(name) # run parent constructor
self.age_weeks = age_weeks
def speak(self):
return super().speak() + " (squeaky)"
for a in [Animal("Generic"), Dog("Rex"), Puppy("Bit", 8)]:
print(a.intro())
print(isinstance(Puppy("x", 1), Animal), issubclass(Dog, Animal))Notice intro() is defined once in Animal yet calls the right speak() for each subclass. That is polymorphism.
Properties: computed and validated attributes
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
@property
def fahrenheit(self):
return self.celsius * 9 / 5 + 32
@fahrenheit.setter
def fahrenheit(self, value):
self.celsius = (value - 32) * 5 / 9
t = Temperature(100)
print(t.fahrenheit) # looks like an attribute, runs a method
t.fahrenheit = 32
print(t.celsius)Dataclasses: less boilerplate
For classes that mainly hold data, @dataclass writes __init__, __repr__ and __eq__ for you:
from dataclasses import dataclass, field
@dataclass(order=True)
class Book:
title: str
pages: int
tags: list = field(default_factory=list)
a = Book("Fluent Python", 1000, ["advanced"])
b = Book("Automate", 500)
print(a)
print(a == Book("Fluent Python", 1000, ["advanced"]))
print(sorted([a, b])[0].title)Classes are a big topic; you can go a long way with just __init__, a few methods and __repr__. Next: what to do when things go wrong — errors and exceptions.
Frequently asked questions
What is self in Python?
self is the instance a method is operating on. It is the first parameter of every instance method and Python fills it in automatically when you call obj.method().
What is the difference between a class and an object?
A class is the blueprint (Account); an object is a concrete thing made from it (Ada's account with balance 120). One class can create many objects.
What does __init__ do?
__init__ is the initializer that runs when an object is created. It usually sets the instance attributes from the arguments you pass to the class.
When should I use inheritance?
When one class genuinely is a specialised kind of another (Dog is an Animal). If you just want to reuse functionality, composition (holding another object as an attribute) is often simpler.