Python Classes Cheat Sheet
1 · The lesson
readA class is a plan for a kind of thing: what data it holds and what it can do. An object is one real thing made from that plan. This page is a short reference with examples you can run. For the full tutorials, see Object-oriented programming, Inheritance and Dataclasses.
1. A class and an object
__init__ runs once, when you make an object. It sets the object's starting data. A method is a function that belongs to the class. An attribute is a piece of data stored on the object.
class Account: def __init__(self, owner, balance=0): self.owner = owner self.balance = balance def deposit(self, amount): self.balance += amount return self.balance acc = Account("Ada", 100) # call the class to make an object print(acc.owner) # Ada print(acc.deposit(50)) # 150 print(acc.balance) # 150 grace = Account("Grace") # each object has its own data print(grace.balance) # 0
2. self: the object itself
Inside a method, self is the object the method was called on. self.balance means "this object's balance". Every method takes self first, but you never pass it yourself: Python does it for you.
acc = Account("Ada", 100) acc.deposit(50) # what you write Account.deposit(acc, 50) # what Python runs: acc goes in as self print(acc.balance) # 200
setup added so this can run · re-runs 1 earlier block on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) Account = _AutoMock('Account') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'class Account:\n def __init__(self, owner, balance=0):\n self.owner = owner\n self.balance = balance\n\n def deposit(self, amount):\n self.balance += amount\n return self.balance\n\nacc = Account("Ada", 100) # call the class to make an object\nprint(acc.owner) # Ada\nprint(acc.deposit(50)) # 150\nprint(acc.balance) # 150\n\ngrace = Account("Grace") # each object has its own data\nprint(grace.balance) # 0', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
3. Printing an object: __repr__ and __str__
Without __repr__ and __str__, printing an object shows something like <__main__.Account object at 0x7f...>. __repr__ is for programmers: the >>> prompt and lists use it. __str__ is for people: print() and f-strings use it.
class Account: def __init__(self, owner, balance=0): self.owner = owner self.balance = balance def deposit(self, amount): self.balance += amount return self.balance def __repr__(self): return f"Account({self.owner!r})" def __str__(self): return f"Owner: {self.owner}" acc = Account("Ada", 100) print(acc) # Owner: Ada print([acc]) # [Account('Ada')] print(repr(acc)) # Account('Ada')
4. Class attributes and instance attributes
An attribute set in the class body is a class attribute: all objects share it. An attribute set on self is an instance attribute: each object has its own.
class Branch: bank = "Northwind" # class attribute: shared by all def __init__(self, city): self.city = city # instance attribute: one per object a, b = Branch("Oslo"), Branch("Lima") print(a.bank, b.bank) # Northwind Northwind print(a.city, b.city) # Oslo Lima Branch.bank = "Eastgate" # change bank on the class print(b.bank) # Eastgate
5. @property
A property is a method that you read like an attribute, without (). The value is calculated again every time you read it.
class Temp: def __init__(self, celsius): self.celsius = celsius @property def fahrenheit(self): return self.celsius * 9 / 5 + 32 t = Temp(20) print(t.fahrenheit) # 68.0 t.celsius = 30 print(t.fahrenheit) # 86.0
t.fahrenheit = 50 raises AttributeError: a property is read-only unless you also write a setter with @fahrenheit.setter.
6. @classmethod and @staticmethod
A class method gets the class as cls, not an object. Use it for another way to make an object. A static method gets no self and no cls: it is a plain function that you keep in the class because it is related to the class.
class Account: def __init__(self, owner, balance=0): self.owner = owner self.balance = balance def deposit(self, amount): self.balance += amount return self.balance @classmethod def from_text(cls, text): owner, balance = text.split(",") return cls(owner, int(balance)) @staticmethod def is_valid(amount): return amount > 0 hedy = Account.from_text("Hedy,250") print(hedy.owner, hedy.balance) # Hedy 250 print(Account.is_valid(-5)) # False
7. Inheritance and super()
A child class (here Savings) is built from a parent class (here Account) and gets all the methods of the parent class. super().__init__(...) runs the parent's __init__, so the child does not have to repeat it.
class Savings(Account): def __init__(self, owner, rate): super().__init__(owner) # sets owner and balance self.rate = rate def add_interest(self): gain = self.balance * self.rate return self.deposit(gain) # deposit() comes from Account s = Savings("Margaret", 0.02) s.deposit(1000) print(s.add_interest()) # 1020.0 print(isinstance(s, Account)) # True print(isinstance(hedy, Savings)) # False
setup added so this can run · re-runs 4 earlier blocks on this page first
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) Account = _AutoMock('Account') hedy = _AutoMock('hedy') # Earlier blocks on this page that this one builds on, re-run quietly first. _pm_earlier = [ 'class Account:\n def __init__(self, owner, balance=0):\n self.owner = owner\n self.balance = balance\n\n def deposit(self, amount):\n self.balance += amount\n return self.balance\n\nacc = Account("Ada", 100) # call the class to make an object\nprint(acc.owner) # Ada\nprint(acc.deposit(50)) # 150\nprint(acc.balance) # 150\n\ngrace = Account("Grace") # each object has its own data\nprint(grace.balance) # 0', 'acc = Account("Ada", 100)\nacc.deposit(50) # what you write\nAccount.deposit(acc, 50) # what Python runs: acc goes in as self\nprint(acc.balance) # 200', 'class Account:\n def __init__(self, owner, balance=0):\n self.owner = owner\n self.balance = balance\n\n def deposit(self, amount):\n self.balance += amount\n return self.balance\n\n def __repr__(self):\n return f"Account({self.owner!r})"\n\n def __str__(self):\n return f"Owner: {self.owner}"\n\nacc = Account("Ada", 100)\nprint(acc) # Owner: Ada\nprint([acc]) # [Account(\'Ada\')]\nprint(repr(acc)) # Account(\'Ada\')', 'class Account:\n def __init__(self, owner, balance=0):\n self.owner = owner\n self.balance = balance\n\n def deposit(self, amount):\n self.balance += amount\n return self.balance\n\n @classmethod\n def from_text(cls, text):\n owner, balance = text.split(",")\n return cls(owner, int(balance))\n\n @staticmethod\n def is_valid(amount):\n return amount > 0\n\nhedy = Account.from_text("Hedy,250")\nprint(hedy.owner, hedy.balance) # Hedy 250\nprint(Account.is_valid(-5)) # False', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
8. @dataclass: the short way
For a class that mostly holds data, @dataclass writes __init__, __repr__ and __eq__ for you from the fields you list.
from dataclasses import dataclass @dataclass class Order: item: str qty: int price: float = 0.0 # a field with a default comes after those without print(Order("tea", 2, 1.5)) # Order(item='tea', qty=2, price=1.5) print(Order("tea", 2) == Order("tea", 2)) # True print(Order("pie", 1).price) # 0.0
9. Special methods
Methods with two underscores on each side let your objects work with Python's own operators and functions.
| Method | Used by |
|---|---|
__len__ | len(obj) |
__eq__ | a == b |
__lt__ | a < b, and so sorted(), min(), max() |
__repr__, __str__ | the >>> prompt, print() |
class Playlist: def __init__(self, name, songs): self.name = name self.songs = songs # the number of songs def __len__(self): return self.songs def __eq__(self, other): return self.songs == other.songs def __lt__(self, other): return self.songs < other.songs rock, jazz = Playlist("rock", 3), Playlist("jazz", 2) print(len(rock)) # 3 print(rock == Playlist("other", 3)) # True print([p.name for p in sorted([rock, jazz])]) # ['jazz', 'rock']
Common mistakes
Forgetting self. def deposit(amount): inside a class looks fine, but acc.deposit(5) raises TypeError: ... takes 1 positional argument but 2 were given, because Python passes the object as well. Write def deposit(self, amount):.
A list as a class attribute. The list is made once and shared by every object:
class Team: members = [] # one list for all teams a, b = Team(), Team() a.members.append("Ada") print(b.members) # ['Ada'] class Team: def __init__(self): self.members = [] # fix: a new list for each object
Forgetting the (). acc.deposit gives you the method itself and runs nothing. Write acc.deposit(50).
Returning a value from __init__. __init__ only sets up the object. If it returns anything other than None, Python raises TypeError: __init__() should return None.
Comparing objects without __eq__. Without __eq__, == asks "is this the same object?":
class Point: def __init__(self, x): self.x = x print(Point(1) == Point(1)) # False
Add an __eq__ method, or use @dataclass, which writes one for you.
Found this useful? Share it with a friend who’s learning Python.