Na dit hoofdstuk snap je:
- wat overerving is in Python en wanneer je het gebruikt,
- het verschil tussen “is-een” en “heeft-een” (inheritance vs compositie),
- hoe method overriding werkt,
- wat
super()precies doet en waarom je het bijna altijd wilt, - en de meest gemaakte fouten (vergeten
super().__init__(), verkeerde “is-a” relatie, rare bugs bij multiple inheritance).
1. Wat is overerving? #
Overerving betekent: je maakt een subclass die eigenschappen en gedrag erft van een base class.
- Base class: algemene versie (bv.
Car) - Subclass: gespecialiseerde versie (bv.
ElectricCar)
class Car:
def start(self):
return "Engine started"
class ElectricCar(Car):
pass
e = ElectricCar()
print(e.start()) # erft start() van CarPythonWaarom zou je dit doen?
- Je wil gedeelde logica op één plek hebben (DRY).
- Je wil een “speciale variant” maken die net iets anders doet.
2. De belangrijkste vraag: “is-een” of “heeft-een”? #
Dit voorkomt 80% van de ontwerp-fouten.
Overerving = “is-een”
ElectricCar is een Car.
Goed:
ElectricCaris eenCarStudentis eenPersonAdminUseris eenUser(soms)
Compositie = “heeft-een”
Een Car heeft een Engine → dus geen inheritance.
Goed:
CarheeftEngineOrderheeftCartItemsEmailServiceheeft eenClient
Vuistregel:
Als je zin logisch klopt met “is een”, dán pas is overerving verdedigbaar.
3. Method overriding: gedrag aanpassen in een subclass #
Een subclass kan een methode overschrijven (override) om ander gedrag te geven.
class Car:
def start(self):
return "Engine started"
class ElectricCar(Car):
def start(self):
return "Electric motor started"
print(Car().start()) # Engine started
print(ElectricCar().start()) # Electric motor startedPythonHier gebeurt iets belangrijks:
- Python kijkt naar het type van het object (runtime).
ElectricCar().start()gebruikt de override.
Dat is al een vorm van polymorfisme.
4. Waar past super() in dit verhaal? #
Veel overrides willen niet alles vervangen, maar iets toevoegen aan het basisgedrag.
Dán gebruik je super().
Waarom niet gewoon Car.start(self) aanroepen?
Dat kan, maar super() is:
- netter (minder hardcoded),
- beter bij refactors,
- essentieel bij multiple inheritance (cooperative).
Voorbeeld: basisgedrag uitbreiden
class Car:
def start(self):
return "Engine started"
class RacingCar(Car):
def start(self):
base = super().start()
return base + " with race mode"
print(RacingCar().start())
# Engine started with race modePythonWat betekent super() hier concreet?
- Het is een manier om “de volgende implementatie in de inheritance-keten” te pakken.
- Niet “de parent class” in het algemeen, maar volgens de MRO (daarover zo meer).
5. De meest voorkomende fout: super().__init__() vergeten #
Als je base class __init__ attributen zet, en jij overschrijft __init__ in een subclass, dan moet je meestal de base init ook draaien.
Fout
class Vehicle:
def __init__(self, brand):
self.brand = brand
class Car(Vehicle):
def __init__(self, brand, doors):
self.doors = doors
c = Car("Volvo", 5)
print(c.brand) # AttributeError: brand bestaat nietPythonJe base class deed het werk (brand zetten), maar je hebt het nooit aangeroepen.
Goed
class Vehicle:
def __init__(self, brand):
self.brand = brand
class Car(Vehicle):
def __init__(self, brand, doors):
super().__init__(brand) # base init
self.doors = doors
c = Car("Volvo", 5)
print(c.brand) # VolvoPythonWaarom is dit “beter ontwerp”?
- Base class blijft verantwoordelijk voor base-state.
- Subclass voegt alleen extra state toe.
- Minder dubbel werk, minder bugs.
6. Wanneer overerving onhandig wordt (en compositie beter is) #
Overerving gaat vaak mis als je overerving gebruikt voor “features” i.p.v. een echte “is-a” relatie.
Slecht teken: je wil meerdere “eigenschappen mixen”
Bijvoorbeeld: je wil een Car die:
- elektrisch is
- én luxe is
- én sport is
Dan krijg je al snel:
LuxuryElectricSportsCar(Dit gaat nooit werken zo…)
Dat is een hint dat je beter compositie gebruikt.
Compositie-voorbeeld: Car + Engine
class Engine:
def __init__(self, kind):
self.kind = kind
def start(self):
return f"{self.kind} engine started"
class Car:
def __init__(self, brand, engine: Engine):
self.brand = brand
self.engine = engine
def start(self):
return self.engine.start()
car = Car("Tesla", Engine("electric"))
print(car.start()) # electric engine startedPythonWaarom dit fijner is dan inheritance?
- Je kunt engine wisselen zonder subclass-explosie.
- Gedrag is “plugbaar”.
7. Multiple inheritance en MRO (alleen wat je écht moet weten) #
Python kan meerdere base classes hebben:
class A: ...
class B: ...
class C(A, B): ...PythonDan moet Python weten: als C een methode niet heeft, zoek ik eerst in A of in B?
Dat regelt Python met de MRO (Method Resolution Order).
Je kunt het zien met:
print(C.__mro__)PythonWaarom is dit relevant voor super()?
Omdat super() niet “gewoon de parent” is, maar “de volgende class in de MRO”.
Dit is precies waarom super() de voorkeur heeft boven BaseClass.method(self).
Praktische tip:
- Gebruik multiple inheritance vooral voor mixins (kleine herbruikbare stukjes gedrag).
- Als je MRO niet snapt, hou het simpel en kies compositie.
8. Voorbeeld: Notifications #
Je wil verschillende notificaties: email, sms, push.
Overerving kan, maar vaak is het slimmer als polymorfisme/compositie. Toch is dit een nette “is-a” case:
class Notifier:
def send(self, message):
raise NotImplementedError
class EmailNotifier(Notifier):
def send(self, message):
print(f"Email: {message}")
class SMSNotifier(Notifier):
def send(self, message):
print(f"SMS: {message}")
def alert(notifier: Notifier, message):
notifier.send(message)
alert(EmailNotifier(), "Hi!")
alert(SMSNotifier(), "Hi!")PythonDit laat zien:
- “is-a”: EmailNotifier is een Notifier
- polymorfisme:
alert()werkt met elke notifier metsend()
Nu je overerving hebt geleerd is het belangrijk om nog meer standaard functies te kennen die betrekking hebben op de verschillende objecten. Zie het volgende hoofdstuk voor de ‘Dunder’ methods.

