class Child(Parent):
def __init__(self):
super().__init__()
example
class Vehicle:
def __init__(self, make: str, year: int):
self.make = make
self.year = year
class ElectricCar(Vehicle):
def __init__(self, make: str, year: int, range_km: int):
super().__init__(make, year)
self.range_km = range_km
def __repr__(self) -> str:
return f"{self.year} {self.make} ({self.range_km}km range)"
car = ElectricCar("Tesla", 2026, 600)
print(car)
output
2026 Tesla (600km range)
Note Always call super().__init__() in the child to ensure the parent is properly initialized. Python supports multiple inheritance via MRO (Method Resolution Order).
inheritancesuper()subclassextend classparent class
class DateRecord:
def __init__(self, year: int, month: int, day: int):
self.year = year
self.month = month
self.day = day
@classmethod
def from_string(cls, date_str: str) -> "DateRecord":
y, m, d = map(int, date_str.split("-"))
return cls(y, m, d)
@staticmethod
def is_valid_year(year: int) -> bool:
return1 <= year <= 9999
record = DateRecord.from_string("2026-04-04")
print(record.year, DateRecord.is_valid_year(2026))
output
2026 True
Note @classmethod receives the class as first arg (cls) and is commonly used for alternative constructors. @staticmethod receives neither self nor cls.
Note Dataclasses auto-generate __init__, __repr__, and __eq__. Use field(default_factory=list) for mutable defaults, never a bare [] as a default.
dataclassdata classauto initstructrecord
__slots__ (Memory Optimization)
syntax
class Name:
__slots__ = ('attr1', 'attr2')
example
@dataclass(slots=True)
class Point:
x: float
y: float
p = Point(1.0, 2.0)
print(p)
# p.z = 3.0 # AttributeError: no __dict__
output
Point(x=1.0, y=2.0)
Note __slots__ prevents dynamic attribute creation and reduces memory by eliminating the per-instance __dict__. Python 3.10+ dataclasses support slots=True directly.
from abc import ABC, abstractmethod
class Base(ABC):
@abstractmethod
def method(self): ...
example
from abc import ABC, abstractmethod
class Shape(ABC):
@abstractmethod
def area(self) -> float: ...
class Circle(Shape):
def __init__(self, radius: float):
self.radius = radius
def area(self) -> float:
return3.14159 * self.radius ** 2# shape = Shape() # TypeError: can't instantiate abstract class
circle = Circle(5)
print(f"Area: {circle.area():.2f}")
output
Area: 78.54
Note Abstract classes cannot be instantiated directly. Subclasses must implement all @abstractmethod methods or they will also be abstract.
abstract classABCinterfaceabstractmethodcontract
Protocols (Structural Typing)
syntax
from typing import Protocol
class Drawable(Protocol):
def draw(self) -> None: ...
example
from typing import Protocol
class Saveable(Protocol):
def save(self, path: str) -> None: ...
class Document:
def save(self, path: str) -> None:
print(f"Saved to {path}")
def backup(item: Saveable, dest: str) -> None:
item.save(dest)
backup(Document(), "/tmp/doc.txt")
output
Saved to /tmp/doc.txt
Note Protocols enable duck-typing with static type checking. Classes do not need to explicitly inherit from the Protocol; they just need matching methods.
Note __repr__ should produce an unambiguous developer-facing string. __str__ is the user-facing version. Return NotImplemented (not raise) from __eq__ for unknown types.