Zelf functies schrijven
Kies duidelijke grenzen voor functies
Nu je een functie kunt definiëren, komt er een nieuwe vraag bij: welke regels horen in een functie?
Er is geen formule die altijd het perfecte antwoord geeft. Er zijn wel nuttige aanwijzingen.
Begin met één duidelijke verantwoordelijkheid
Kijk terug naar het menu uit hoofdstuk 7. De opdrachten benoemen al afzonderlijke verantwoordelijkheden:
| Opdracht | Verantwoordelijkheid | Mogelijke functie |
|---|---|---|
add | één item vragen en opslaan | add_item(items) |
list | de opgeslagen items tonen | show_items(items) |
count | tonen hoeveel items zijn opgeslagen | show_count(items) |
Die grenzen zijn natuurlijk, omdat elke functienaam één volledige actie beschrijft.
Vergelijk dat met:
def handle_everything():
# print the title
# run the menu loop
# add items
# list items
# count items
# say goodbye
pass
Dit is technisch gezien een functie, maar de naam en het blok verbergen nog steeds het hele programma op één plek. Veel verantwoordelijkheden in een functie stoppen maakt ze niet begrijpelijker.
Aan het andere uiterste:
def print_number(number):
print(number)
def add_one(number):
return number + 1
Heel kleine functies kunnen nuttig zijn, maar elke eenvoudige expressie opsplitsen kan de lezer dwingen tussen namen te springen zonder daar iets aan te hebben.
Een goede eerste vraag is:
Kan ik de taak van deze functie beschrijven met één korte zin die met een werkwoord begint?
Show the stored items voldoet: toon de opgeslagen items. Handle the menu and all its commands and also print the final report is een waarschuwing dat meerdere verantwoordelijkheden door elkaar lopen: het menu afhandelen, alle opdrachten uitvoeren én het eindrapport afdrukken.
Bepaal wat de grens oversteekt
Als de taak duidelijk is, stel je nog twee vragen:
Welke informatie heeft de functie nodig?
Heeft de aanroeper een waarde terug nodig?
Bijvoorbeeld:
def count_message(items):
return f"Items stored: {len(items)}"
De functie heeft de lijst nodig, dus items is een parameter. De aanroeper heeft de complete tekst nodig, dus de functie geeft die terug.
Deze functie heeft een andere verantwoordelijkheid:
def show_count(items):
print(f"Items stored: {len(items)}")
Ook deze functie heeft items nodig, maar haar taak is specifiek om het aantal te tonen. Ze drukt af en hoeft het bericht niet terug te geven.
Beide ontwerpen kunnen goed zijn. Kies op basis van wat de aanroeper nodig heeft.
Houd het hoofdpad leesbaar
Het doel van het herstructureren in hoofdstuk 8 is niet om het totale aantal regels te verminderen. Functiedefinities kunnen het bestand iets langer maken.
De verbetering zie je in de menulus:
if command == "add":
add_item(items)
elif command == "list":
show_items(items)
elif command == "count":
show_count(items)
De lus leest nu als een overzicht van keuzes. Wie het algemene plan wil begrijpen, kan daar blijven. Wie de details van het tonen van de lijst wil weten, kan direct naar show_items gaan.
Dat is structuur: verschillende detailniveaus, elk met een duidelijke plek.
Een praktische checklist
Stel jezelf deze vragen voordat je code naar een functie verplaatst:
Kan ik de bewerking een nauwkeurige naam geven?
Heeft ze één hoofdverantwoordelijkheid?
Zijn de benodigde parameters duidelijk?
Is duidelijk of ze iets moet afdrukken, teruggeven of wijzigen?
Wordt de aanroeper makkelijker te lezen?
Je wordt beter in deze keuzes door ze te maken, het resultaat te lezen en het te verbeteren.
Welke functie heeft de duidelijkste afzonderlijke verantwoordelijkheid?
Een functie bouwt een opgeschoonde opdracht die een if-instructie gaat vergelijken. Wat moet de functie doen?