Classes, dunder methods, properties, inheritance and ABCs, dataclasses, enums and attribute introspection.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)__init__ sets up the instance; self is the instance.
class Counter:
total = 0 # class attribute, shared
def __init__(self):
Counter.total += 1
Counter(); Counter(); Counter.totalClass attribute vs instance attribute → 2.
class Point:
def __init__(self, x, y): self.x, self.y = x, y
def __repr__(self):
return f"Point({self.x}, {self.y})"
repr(Point(1, 2))__repr__ is the debug form; __str__ falls back to it.
class Temp:
def __init__(self, c): self.c = c
@classmethod
def from_f(cls, f):
return cls((f - 32) * 5 / 9)
Temp.from_f(212).cAlternate constructor → 100.0.
class Maths:
@staticmethod
def add(a, b): return a + b
Maths.add(2, 3)No self/cls; a plain function namespaced on the class.
class Circle:
def __init__(self, r): self._r = r
@property
def area(self): return 3.14159 * self._r ** 2
@property
def r(self): return self._r
@r.setter
def r(self, v):
if v < 0: raise ValueError("negative")
self._r = v
c = Circle(1); c.r = 2; c.areaAttribute syntax with computed get and validated set.
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y): self.x, self.y = x, yFixed attributes, less memory, no __dict__.
class Money:
def __init__(self, v): self.v = v
def __eq__(self, o): return isinstance(o, Money) and self.v == o.v
def __hash__(self): return hash(self.v)
Money(5) == Money(5), len({Money(5), Money(5)})Define __hash__ with __eq__ or instances stop being hashable.
from functools import total_ordering
@total_ordering
class V:
def __init__(self, n): self.n = n
def __eq__(self, o): return self.n == o.n
def __lt__(self, o): return self.n < o.n
V(1) <= V(2)total_ordering fills in the other comparisons.
class Deck:
def __init__(self, cards): self.cards = cards
def __len__(self): return len(self.cards)
def __getitem__(self, i): return self.cards[i]
def __contains__(self, c): return c in self.cards
d = Deck(["A", "K"]); len(d), d[0], "K" in dContainer protocol: len(), indexing and in. __getitem__ also makes it iterable.
class Vec:
def __init__(self, x, y): self.x, self.y = x, y
def __add__(self, o): return Vec(self.x + o.x, self.y + o.y)
def __mul__(self, k): return Vec(self.x * k, self.y * k)
v = Vec(1, 2) + Vec(3, 4); (v * 2).xOperator overloading → 8.
class Timer:
def __enter__(self): print("start"); return self
def __exit__(self, exc_type, exc, tb): print("stop"); return False
with Timer():
passContext manager; return True from __exit__ to swallow an exception.
class Adder:
def __init__(self, k): self.k = k
def __call__(self, x): return x + self.k
Adder(10)(5)__call__ makes instances callable → 15.
class Countdown:
def __init__(self, n): self.n = n
def __iter__(self):
while self.n > 0:
yield self.n; self.n -= 1
list(Countdown(3))__iter__ as a generator is the shortest way to make an iterable.
class Cfg:
def __getattr__(self, name):
return f"<no {name}>"
Cfg().anything__getattr__ runs only for attributes that are not found normally.
class Animal:
def __init__(self, name): self.name = name
def speak(self): return "..."
class Dog(Animal):
def __init__(self, name, breed):
super().__init__(name)
self.breed = breed
def speak(self): return "Woof"
Dog("Rex", "lab").speak()Subclass, call the parent's __init__ with super(), override speak.
isinstance(Dog("a", "b"), Animal), issubclass(Dog, Animal)→ True, True; isinstance also accepts a tuple of classes.
type(Dog("a", "b")).__name__, Dog.__mro__Runtime class name; the method resolution order.
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self): ...
class Sq(Shape):
def __init__(self, s): self.s = s
def area(self): return self.s ** 2
Sq(3).area()Shape() itself raises TypeError; subclasses must implement area.
class A:
def who(self): return "A"
class B(A):
def who(self): return "B" + super().who()
class C(A):
def who(self): return "C" + super().who()
class D(B, C): pass
D().who()Multiple inheritance follows the MRO → 'BCA'.
from typing import Protocol
class HasArea(Protocol):
def area(self) -> float: ...
def total(shapes: list[HasArea]) -> float:
return sum(s.area() for s in shapes)Structural typing: anything with area() fits, no inheritance needed.
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int = 0
User("Ada", 36)Generates __init__, __repr__ and __eq__ from the annotations.
from dataclasses import dataclass, field
@dataclass
class Cart:
items: list[str] = field(default_factory=list)
Cart().itemsMutable defaults must use default_factory.
@dataclass(frozen=True, order=True)
class Version:
major: int
minor: int
Version(1, 2) < Version(1, 10), hash(Version(1, 2)) is not Nonefrozen → immutable and hashable; order → comparisons.
from dataclasses import asdict, replace
u = User("Ada", 36)
asdict(u), replace(u, age=37)To a dict; a modified copy.
@dataclass(slots=True, kw_only=True)
class Opts:
debug: bool = False
Opts(debug=True)slots for memory; kw_only forces keyword construction (3.10+).
from typing import NamedTuple
class Pt(NamedTuple):
x: int
y: int
p = Pt(1, 2); p.x, p[1], tuple(p)Immutable, unpackable, indexable record.
from enum import Enum, auto
class Color(Enum):
RED = auto()
GREEN = auto()
Color.RED.name, Color.RED.value, Color["GREEN"], list(Color)Named constants; iterate or look up by name or value.
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
vars(User("Ada", 36))The instance __dict__ → {'name': 'Ada', 'age': 36}.
u = User("Ada", 36)
getattr(u, "age"), getattr(u, "nope", None), hasattr(u, "name")Attribute access by string name, with a default.
setattr(u, "age", 40); u.ageSet by string name → 40.
from functools import cached_property
class Data:
@cached_property
def heavy(self):
print("computing"); return 42
d = Data(); d.heavy; d.heavyComputed once per instance, then stored.
[m for m in dir(User) if not m.startswith("_")]Public attribute names on a class.
u.__class__ is User, User.__name__, User.__module__Where an object came from.
Want the topic explained, not just listed? The Classes & Objects lesson walks through it with graded exercises. Preparing for interviews? See the interview prep track.