Hoofdstuk 9 - Classes
01 - Een class maken en gebruiken
Intro
- Een class is een blauwdruk of ontwerp voor objecten, zoals het algemene idee “Dier”
- Een class beschrijft welke eigenschappen (bijv. naam, leeftijd) en vaardigheden (bijv. geluid maken) alle dieren hebben
- Een object is een concreet voorbeeld van een class, zoals een echte hond of kat
- Objecten noemen we ook wel instanties van de class
- Je kunt van één class heel veel instanties maken: elke hond is anders, maar volgt hetzelfde ontwerp
- Een class fungeert als de “moeder”, en elke instantie is een “kind” dat eigenschappen van de moeder overneemt
- Als je de moeder-class aanpast (bijvoorbeeld het standaardgeluid), dan veranderen alle kinderen automatisch mee
- Classes kunnen ook van elkaar erven: “Hond” kan kind zijn van “Dier”, en “Labrador” kan weer een kind zijn van “Hond”
- Door overerving kun je gedrag steeds verder specialiseren zonder opnieuw alle code te schrijven
- Met classes kun je programma’s beter organiseren, uitbreiden en hergebruiken, precies zoals functies dat ook doen — maar dan op een groter niveau
+------------------+
| Dier | ← class (moeder)
|------------------|
| eigenschappen: |
| - naam |
| - leeftijd |
|------------------|
| methoden: |
| - zitten |
| - omrollen |
+------------------+
|
-------------------------
| |
v v
+-----------+ +---------------+
| hond | | kat |
| (kind) | | (kind) |
+-----------+ +---------------+
CLASS-HIERARCHIE
--------------------------------------
+-------------+
| Dier | ← moeder
+-------------+
|
v
+-------------+
| Hond | ← kind van Dier
+-------------+
|
v
+----------------+
| Labrador | ← kind van Hond
+----------------+
Labrador
|
-----------------------------------
| | |
v v v
+----------+ +----------+ +-----------+
| max | | luna | | diesel |
| object | | object | | object |
+----------+ +----------+ +-----------+
- Complete boom: drie “moeders” boven elkaar → één kind-object
(LEVEL 1) CLASS
+------------------------+
| Dier |
+------------------------+
|
v
(LEVEL 2) SUBCLASS
+------------------------+
| Hond |
+------------------------+
|
v
(LEVEL 3) SUB-SUBCLASS
+------------------------+
| Labrador |
+------------------------+
|
v
(LEVEL 4) INSTANTIE (object)
+------------------------+
| max = Labrador() |
+------------------------+
- In de praktijk gebruik je meestal 1 tot 3 niveaus van overerving (bijv.
Dier → Hond → Labrador); diepere ketens maken de code moeilijker te begrijpen en te onderhouden - Een subclass moet logisch een “is een” relatie hebben met zijn parent — een hond is een dier, maar een dierenwinkel is geen dier. Als de relatie niet klopt, klopt de OOP-structuur meestal ook niet
class Dog:
"""Een eenvoudige representatie van een hond."""
def __init__(self, name, age):
"""Initialiseer de hond met een naam en een leeftijd."""
self.name = name
self.age = age
def sit(self):
"""Laat de hond gaan zitten."""
print(self.name.title() + " gaat nu zitten.")
def roll_over(self):
"""Laat de hond omrollen."""
print(self.name.title() + " rolt om!")
- Classnamen worden met een hoofdletter geschreven
- De docstring met drie aanhalingstekens ("""...""") is de officiële manier en wordt o.a. gebruikt door documentatiegenerators
- Functies binnen een class heten methoden
__init__()is een speciale (dunder) methode die wordt uitgevoerd bij het maken van een object- De dubbele underscores zijn verplicht: Python herkent deze naam als de constructor
__init__()heeft in dit voorbeeld drie parameters:self,name,ageselfmoet altijd de eerste parameter zijn; het verwijst naar het concrete object- Bij het aanroepen van een methode wordt
selfautomatisch door Python ingevuld - Wanneer je een object van
Dogmaakt, geef je dus alleennameenagemee - Variabelen die met
self.worden opgeslagen, heten attributen en zijn binnen de hele class beschikbaar - Deze attributen kun je ook buiten de class via het object benaderen, bijvoorbeeld
hond.name
- De class Dog heeft nog twee methoden: sit() en roll_over()
- Voor deze twee methoden is alleen
selfals parameter nodig - De objecten die later gemaakt worden, hebben toegang tot deze methoden
hond1 = Dog('willie', 6)
hond2 = Dog('lucy', 3)
print("Mijn hond heet " + hond1.name.title() + ".")
print("Mijn hond is " + str(hond1.age) + " jaar oud.")
hond1.sit()
print("Jouw hond heet " + hond2.name.title() + ".")
print("Jouw hond is " + str(hond2.age) + " jaar oud.")
hond2.sit()
Mijn hond heet Willie.
Mijn hond is 6 jaar oud.
Willie gaat nu zitten.
Jouw hond heet Lucy.
Jouw hond is 3 jaar oud.
Lucy gaat nu zitten.
- De variabele
hond1wordt nu gevuld met het object dat gemaakt wordt door de class - Classes schrijf je met een hoofdletter (PascalCase), variabelen en functies schrijf je met kleine letters (snake_case)
- Bij het aanroepen van de class worden de juiste argumenten meegegeven aan de
__init__()-methode - De
__init__()methode heeft geen specifieke return-instructie, maar Python retourneert automatisch een nieuw object dat deze hond voorstelt
- Met de puntnotatie kun je de waarde van een attribuut vinden
hond1.nameis hetzelfde attribuut alsself.namein de__init__()methode- Na het maken van een object van de class
Dogkun je puntnotatie gebruiken om elke methode in de class aan te roepen
hond1.sit()
hond1.roll_over()
Willie gaat nu zitten.
Willie rolt om!
- Je kunt van een class zoveel objecten maken als je nodig hebt
- Ook als een tweede hond dezelfde naam en dezelfde leeftijd heeft, wordt er toch een nieuw object gemaakt
- De variabele waarin je het hond-object opslaat, moet wel een unieke naam hebben
class Dog:
- name
- age
- sit()
- roll_over()
|
| Maak een object
v
hond = Dog("Fifi", 3)
hond ---> [ Dog-object ]
name = "Fifi"
age = 3
methoden: sit(), roll_over()
Gebruik:
hond.name
hond.age
hond.sit()
02 - Werken met classes en objecten
class Car():
"""Eenvoudige representatie van een auto"""
def __init__(self, manufacturer, model, year):
"""Initialiseren van de attributen die een auto beschrijven"""
self.manufacturer = manufacturer
self.model = model
self.year = year
def get_descriptive_name(self):
"""Retourneer een mooi opgemaakte beschrijvende naam"""
long_name = str(self.year) + ' ' + self.manufacturer + ' ' + self.model
return long_name.title()
my_new_car = Car('Landrover', '90', 2022)
print(my_new_car.get_descriptive_name())
2022 Landrover 90
- Je kunt een attribuut toevoegen aan de class dat gaandeweg verandert: de kilometerstand
- Elk attribuut in een class moet een initiële waarde hebben, zelfs als het 0 is of een lege string
- Als je een standaardwaarde specificeert voor een attribuut, hoef je deze bij het aanroepen niet mee te geven
- Om de kilometerstand af te lezen plaatsen we een extra methode read_odometer()
class Car():
"""Eenvoudige representatie van een auto"""
def __init__(self, manufacturer, model, year):
"""Initialiseren van de attributen die een auto beschrijven"""
self.manufacturer = manufacturer
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
"""Retourneer een mooi opgemaakte beschrijvende naam"""
long_name = str(self.year) + ' ' + self.manufacturer + ' ' + self.model
return long_name.title()
def read_odometer(self):
"""Print het aantal kilometers"""
print("Deze auto heeft " + str(self.odometer_reading) + " kilometers gereden.")
my_new_car = Car('Landrover', '90', 2022)
print(my_new_car.get_descriptive_name())
my_new_car.read_odometer()
2022 Landrover 90
Deze auto heeft 0 kilometers gereden.
- Je kunt de waarde van een attribuut op drie manieren wijzigen
- De waarde instellen rechtstreeks via het object
- De waarde instellen via een methode
- De waarde aanpassen met een methode
# 1 - Attributen van het object rechtstreeks aanpassen
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
Deze auto heeft 23 kilometers gereden
# 2 - De waarde van een attribuut instellen met een extra methode in de class
def update_odometer(self, km):
"""Stel de kilometerteller in op de opgegeven waarde."""
if km >= self.odometer_reading:
self.odometer_reading = km
else:
print("Je kunt je kilometerteller niet terugdraaien!")
# Aanroepen van nieuwe methode update_odometer()
my_new_car.update_odometer(23)
my_new_car.read_odometer()
Deze auto heeft 23 kilometers gereden
# 3 - De waarde van een attribuut aanpassen met een extra methode in de class
def decrement_odometer(self, km):
"""Verlaag de kilometerteller met de opgegeven kilometers"""
self.odometer_reading -= km
# Aanroepen van nieuwe methode decrement_odometer()
my_new_car.decrement_odometer(10)
my_new_car.read_odometer()
Deze auto heeft 13 kilometers gereden.
03 - Overerven
- Waarom gebruik je overerving? Om code te hergebruiken: je zet gedeelde eigenschappen en methoden in een moederclass, zodat je die niet opnieuw hoeft te schrijven in elke subclass
- Wanneer gebruik je overerving? Wanneer er een logische “is-een”-relatie bestaat tussen twee dingen: een hond is een dier, een sportauto is een auto; maar een dierenwinkel is geen dier
- Hoe gebruik je overerving? Je laat een class erven door de parent-class tussen haakjes achter de subclass te zetten, bijvoorbeeld
class Hond(Dier):, waardoor de subclass automatisch alle eigenschappen en methoden van de parent-class overneemt
class Car():
"""Eenvoudige representatie van een auto"""
def __init__(self, manufacturer, model, year):
"""Initialiseren van de attributen die een auto beschrijven"""
self.manufacturer = manufacturer
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
"""Retourneer een mooi opgemaakte beschrijvende naam"""
long_name = str(self.year) + ' ' + self.manufacturer + ' ' + self.model
return long_name.title()
def read_odometer(self):
"""Print het aantal kilometers"""
print("Deze auto heeft " + str(self.odometer_reading) + " kilometers gereden.")
def update_odometer(self, km):
"""Stel de kilometerteller in op de opgegeven waarde."""
if km >= self.odometer_reading:
self.odometer_reading = km
else:
print("Je kunt je kilometerteller niet terugdraaien!")
def decrement_odometer(self, km):
"""Verlaag de kilometerteller met de opgegeven kilometers"""
self.odometer_reading -= km
class ElectricCar(Car):
"""Representeert een auto, specifiek voor elektrische auto's."""
def __init__(self, manufacturer, model, year):
"""Initialiseer eerst de attributen van de moederclass,
en daarna de attributen die specifiek zijn voor elektrische auto's.
"""
super().__init__(manufacturer, model, year)
# hier komen straks de extra ElectricCar-attributen
my_car = ElectricCar('Landrover', 'defender', '2030')
print(my_car.get_descriptive_name())
2030 Landrover Defender
- De parent-class moet al gedefinieerd zijn voordat je de child-class kunt maken
- De functie
super()geeft vanuit de child-class toegang tot de methoden van de parent-class super().__init__()voert de constructor van de parent-class uit- Daarna kun je in de child-class extra attributen toevoegen
- Het woord “super” komt van de term superclass: de class waarvan een andere class erft
class ElectricCar(Car):
"""Representeert een auto, specifiek voor elektrische auto's."""
def __init__(self, manufacturer, model, year):
"""Initialiseer eerst de attributen van de moederclass,
en daarna de attributen die specifiek zijn voor elektrische auto's.
"""
super().__init__(manufacturer, model, year)
# hier komen straks de extra ElectricCar-attributen
self.battery_size = 70
def describe_battery(self):
"""Print hoe groot de batterij is."""
print("Deze auto heeft een " + str(self.battery_size) + " kWh batterij")
my_car = ElectricCar('Landrover', 'defender', '2030')
print(my_car.get_descriptive_name())
my_car.describe_battery()
2030 Landrover Defender
Deze auto heeft een 70 Kwh batterij
- Het nieuwe attribuut
battery_sizevoor de elektrische auto heeft een standaardwaarde van 70 - Dit attribuut is alleen beschikbaar in de subclass
ElectricCar - De nieuwe methode
describe_battery()is ook alleen beschikbaar in de classElectricCar
Methoden uit de parent-class overschrijven
- Je kunt elke methode uit een parent-class overschrijven als je deze niet wilt gebruiken in de child-class
- Je doet dit door in de child-class een methode te definiëren met precies dezelfde naam
- Stel: de class
Carheeft een methode met de naamfill_gas_tank() - Deze methode is zinloos voor een volledig elektrische auto
class ElectricCar(Car):
...
def fill_gas_tank(self):
"""Elektrische auto's hebben geen brandstoftank."""
print("Deze auto heeft geen brandstoftank!")
- Als iemand nu
fill_gas_tank()aanroept op eenElectricCar-object, krijgt hij een duidelijke melding - Met overerving kun je een child-class precies laten behouden wat nodig is en de rest overschrijven
Objecten als attributen (compositie)
- Wanneer je steeds meer details toevoegt aan een class, kan deze onbedoeld te groot en onoverzichtelijk worden
- Het kan dan handiger zijn om bepaalde eigenschappen of onderdelen in een aparte class onder te brengen
- Op deze manier kunnen verschillende classes met elkaar samenwerken, en blijft elke class overzichtelijk en gericht op één taak
- Als je steeds meer batterijgegevens aan de class
ElectricCartoevoegt, wordt de class al snel te lang - Verplaats dan de batterij-attributen en batterij-methoden naar een aparte class, bijvoorbeeld
Batterij - Vervolgens maak je in
ElectricCareen attribuut dat eenBatterij-object bevat, zodat beide classes netjes samenwerken
class Car:
"""Eenvoudige representatie van een auto."""
def __init__(self, manufacturer, model, year):
"""Initialiseren van de attributen die een auto beschrijven."""
self.manufacturer = manufacturer
self.model = model
self.year = year
self.odometer_reading = 0
def get_descriptive_name(self):
"""Retourneer een mooi opgemaakte beschrijvende naam."""
long_name = str(self.year) + ' ' + self.manufacturer + ' ' + self.model
return long_name.title()
def read_odometer(self):
"""Print het aantal kilometers."""
print("Deze auto heeft " + str(self.odometer_reading) + " kilometers gereden.")
def update_odometer(self, km):
"""Stel de kilometerteller in op de opgegeven waarde."""
if km >= self.odometer_reading:
self.odometer_reading = km
else:
print("Je kunt je kilometerteller niet terugdraaien!")
def decrement_odometer(self, km):
"""Verlaag de kilometerteller met de opgegeven kilometers."""
self.odometer_reading -= km
class Battery:
"""Representeert een batterij voor een elektrische auto."""
def __init__(self, battery_size=60):
"""Initialiseren van de attributen van de batterij."""
self.battery_size = battery_size
def describe_battery(self):
"""Print hoe groot de batterij is."""
print("Deze auto heeft een " + str(self.battery_size) + " kWh batterij.")
class ElectricCar(Car):
"""Representeert een auto, specifiek voor elektrische auto's."""
def __init__(self, manufacturer, model, year):
"""Initialiseer eerst de attributen van de moederclass,
en daarna de attributen die specifiek zijn voor elektrische auto's.
"""
super().__init__(manufacturer, model, year)
self.battery = Battery() # standaard batterij van 60 kWh
my_car = ElectricCar('Landrover', 'Defender', 2030)
print(my_car.get_descriptive_name())
my_car.battery.describe_battery()
2030 Landrover Defender
Deze auto heeft een 60 kWh batterij.
- Voor elk
ElectricCar-object wordt nu automatisch eenBatterij-object aangemaakt - Dit lijkt veel extra werk, maar nu kun je de
Batterij-class zo gedetailleerd beschrijven als je wilt, zonder dat deElectricCar-class rommelig wordt - Zo kun je eenvoudig nieuwe methoden toevoegen aan de
Batterij-class, zoals hieronder de methodeget_range()
def get_range(self):
"""Print hoeveel kilometers deze batterij kan rijden."""
if self.battery_size == 60:
range = 140
elif self.battery_size == 85:
range = 185
message = "Deze auto kan ongeveer " + str(range)
message += " kilometer rijden als hij volledig is opgeladen."
print(message)
# het aanroepen van de methode
my_car.battery.get_range()
Deze auto kan ongeveer 140 kilometer rijden als hij volledig is opgeladen.
Is het bereik van een auto een eigenschap van de auto of van de accu?
Overerving vs. Compositie
- Overerving gebruik je wanneer er een logische “is-een” relatie bestaat: een hond is een dier, een elektrische auto is een auto
- Compositie gebruik je wanneer een object andere objecten “bezit”: een auto heeft een batterij, een laptop heeft een scherm
- Bij overerving neemt de child-class attributen en methoden over van de parent-class, waardoor je gedrag uitbreidt of specialiseert
- Bij compositie voeg je een object toe als attribuut in een andere class (bijv.
self.battery = Battery()), waardoor de code overzichtelijk blijft en elke class één duidelijke taak heeft - Je kiest compositie wanneer overerving onlogisch wordt of tot te veel “kunstmatige” subclasses zou leiden. Je kiest overerving wanneer je object echt een gespecialiseerde versie is van een ander object
OVERERVING (is-een)
-----------------------------------
+-----------+
| Car | ← parent
+-----------+
|
v
+----------------+
| ElectricCar | ← child
+----------------+
(is een auto)
COMPOSITIE (heeft-een)
-----------------------------------
+----------------+
| ElectricCar |
+----------------+
|
heeft een |
v
+----------------+
| Battery |
+----------------+
ElectricCar IS EEN Car
ElectricCar HEEFT EEN Battery
04 - Classes importeren
- Bestanden met veel classes kunnen erg lang worden, zelfs als je overerving correct gebruikt
- Om alles overzichtelijk te houden kun je classes opslaan in aparte modules (losse Python-bestanden)
- Wanneer je een class nodig hebt, kun je deze eenvoudig uit een module importeren
- Stel dat we de class
Carin het bestandcar.pyzetten. In hetzelfde mapje staat danmy_car.pymet de volgende code:
from car import Car
my_new_car = Car('Landrover', 'Defender 90', 2030)
print(my_new_car.get_descriptive_name())
my_new_car.odometer_reading = 23
my_new_car.read_odometer()
2030 Landrover Defender 90
Deze auto heeft 23 kilometers gereden.
- Je kunt classes in aparte bestanden (modules) opslaan om je project overzichtelijk te houden
- Met
from bestandsnaam import Classnaamkun je een class in een andere file gebruiken - Het bestand moet in dezelfde map staan, of in een map die door Python gevonden kan worden
Meer over importeren
- Python zoekt modules eerst in dezelfde map als het script dat je uitvoert; dat is altijd het startpunt voor alle imports
- Daarna zoekt Python in de mappen die in
sys.pathstaan, zoals de huidige werkmap, de standaardbibliotheek en de map van je virtual environment - Een importregel verwijst altijd naar een module-naam (bijv.
from car import Car) en niet naar een bestandsnaam of pad; Python accepteert dus geen imports zoalsfrom ../map/car import Car - Wanneer een class zoals
Carin een apart bestand (car.py) staat, werkt de import alleen als dit bestand zich in dezelfde map bevindt als het script dat je uitvoert - Een module kan meerdere classes bevatten; bijvoorbeeld
car.pykanCar,ElectricCarenBatterytegelijk bevatten - Je kunt meerdere classes uit dezelfde module importeren door ze comma-gescheiden te noemen, zoals
from car import Car, ElectricCar, Battery - Je kunt ook de hele module importeren met
import car; je gebruikt de classes dan via de module-namespace, bijvoorbeeldcar.Car(...).- Een namespace is een verzameling namen (variabelen, functies, classes) die bij elkaar horen, zodat Python precies weet welke naam waarnaar verwijst
- Namespaces voorkomen conflicten: verschillende modules mogen dezelfde naam gebruiken (bijv. twee keer
Car), omdat ze in hun eigen namespace leven, zoalscar.Carenrental.Car - Elke module krijgt automatisch een eigen namespace: bij
import cargebruikt je code alles viacar., bijvoorbeeldcar.Car() - Namespaces houden je code overzichtelijk: ze zorgen dat modules, classes en functies gestructureerd blijven en duidelijk maken waar elke naam vandaan komt
- Het importeren van alle namen met
from car import *wordt afgeraden, omdat het onduidelijk maakt welke namen uit de module komen, naamconflicten kan veroorzaken en de leesbaarheid van je code vermindert - Modules kunnen ook andere modules importeren; bijvoorbeeld
electric_car.pykanfrom battery import Batterybevatten, zolang beide modules binnen een geldige zoeklocatie staan - Wil je modules uit andere mappen importeren, dan moet die map vindbaar zijn voor Python — een map is vindbaar als hij voorkomt in
sys.path - Je kunt een map vindbaar maken door een package-structuur te gebruiken: plaats modules in een map met een
__init__.py-bestand en importeer daarna via paden zoalsfrom voertuigen.car import Car.- Een package is simpelweg een map met Python-bestanden en een
__init__.py; daardoor herkent Python de map als importeerbaar pakket - Een
__init__.py-bestand mag volledig leeg zijn; het enige doel is dat Python de map als package herkent - De mappenstructuur bepaalt het importpad: een bestand in
voertuigen/car.pywordt geïmporteerd alsvoertuigen.car - Packages voorkomen naamconflicten en houden code georganiseerd, bijvoorbeeld door auto-gerelateerde classes in een eigen pakket
voertuigente bewaren
- Een package is simpelweg een map met Python-bestanden en een
- Een map kan ook tijdelijk worden toegevoegd aan
sys.pathmetsys.path.append("pad/naar/map"), zodat Python daar modules kan vinden - Als je je programma start vanuit de hoofdmap van je project, wordt die map automatisch opgenomen in
sys.pathen kunnen alle modules binnen die structuur eenvoudiger geïmporteerd worden - Het is belangrijk om te onthouden dat Python modules zoekt vanaf de locatie waar het script wordt uitgevoerd, niet vanaf de locatie van de file waarin de importregel staat
- Door modules logisch op te splitsen en imports correct te gebruiken, blijft je project overzichtelijk, schaalbaar en duidelijk voor jezelf en anderen
05 - De standaardbibliotheek van Python
- Python wordt geleverd met een uitgebreide standaardbibliotheek: honderden modules die je direct kunt gebruiken zonder iets te installeren
- Deze modules bevatten oplossingen voor veelvoorkomende taken, zoals bestandsbeheer, datum- en tijdverwerking, wiskunde, netwerkverkeer en data-analyse
- De standaardbibliotheek maakt Python krachtig: je hoeft veel functionaliteit niet zelf te programmeren, maar kunt bestaande modules hergebruiken
- Je importeert modules uit de standaardbibliotheek net zoals je eigen modules, bijvoorbeeld met
import osoffrom datetime import datetime - Omdat deze modules altijd beschikbaar zijn, kun je in elk Python-project meteen starten met professionele bouwstenen voor vrijwel elke taak
from datetime import datetime
# haal het huidige datum- en tijdstip op
nu = datetime.now()
print("Het is nu:")
print(nu)
# mooi geformatteerde datum en tijd
mooie_datum = nu.strftime("%d-%m-%Y")
mooie_tijd = nu.strftime("%H:%M:%S")
print("Datum:", mooie_datum)
print("Tijd:", mooie_tijd)
Het is nu:
2025-02-14 12:48:23.507391
Datum: 14-02-2025
Tijd: 12:48:23
Meest gebruikte modules uit de standaard bibliotheek
-
os — werken met bestanden en mappen
-
datetime — werken met datum en tijd
-
math — wiskundige functies
-
random — willekeurige waarden
-
collections — handige datastructuren
06 - Classes opmaken
- Namen van classes worden geschreven in PascalCase (bijv.
ElectricCarofDogHouse) - Namen van objecten, variabelen, functies en modules worden in kleine letters met underscores geschreven (snake_case), bijv.
my_carofread_odometer - Elke class hoort een docstring te hebben die direct onder de class-definitie staat en uitlegt waarvoor de class bedoeld is
- Elke module (Python-bestand) kan ook een docstring hebben bovenaan, waarin je kort beschrijft wat er in die module zit (zoals welke classes of functies)
- Gebruik witregels om je code overzichtelijk te maken, maar overdrijf niet. Twee witregels tussen classes, één witregel tussen methoden binnen een class
- Plaats imports bovenaan het bestand: eerst standaardbibliotheek-modules, dan een lege regel, dan externe modules, dan opnieuw een lege regel, en dan pas je eigen modules (PEP 8-importvolgorde)
PEP 8 (Python Enhancement Proposal nr. 8) is de officiële stijlrichtlijn voor Python-code. Het beschrijft hoe je code leesbaar, consistent en onderhoudbaar houdt door afspraken te maken over zaken zoals naamgeving (zoals snake_case en PascalCase), inspringing, witregels, lengte van regels, import-volgorde, documentatie (docstrings) en algemene opmaak. Het doel is dat alle Python-programmeurs wereldwijd dezelfde stijl gebruiken, zodat code er uniform uitziet en veel makkelijker te begrijpen, te debuggen en samen te ontwikkelen is.