Functies
Contents
3.1. Functies#
We hebben al eerder gezien dat we regels code herhaaldelijk kunnen uitvoeren met behulp van for- of while-loops. Als we bijvoorbeeld bepaalde hoeken die in radialen staan naar graden willen omrekenen, kunnen we de volgende code schrijven.
import math
# Print de hoeken [0, 1/4 pi, 1/2 pi, 3/4 pi, pi] in radialen en daarna in graden.
for i in range(5):
hoek_radialen = (i/4) * math.pi
print('De hoek in radialen is: ', (i/4), '* pi = ', hoek_radialen)
hoek_graden = 180 * hoek_radialen / math.pi
print('De hoek in graden is: ', hoek_graden, '\n')
De hoek in radialen is: 0.0 * pi = 0.0
De hoek in graden is: 0.0
De hoek in radialen is: 0.25 * pi = 0.7853981633974483
De hoek in graden is: 45.0
De hoek in radialen is: 0.5 * pi = 1.5707963267948966
De hoek in graden is: 90.0
De hoek in radialen is: 0.75 * pi = 2.356194490192345
De hoek in graden is: 135.0
De hoek in radialen is: 1.0 * pi = 3.141592653589793
De hoek in graden is: 180.0
Soms is het echter zo dat we een bepaalde operatie meerdere keren op dezelfde manier (met dezelfde code) uit willen voeren, maar is dit niet handig in een loop te verwerken. In de onderstaande code wordt bijvoorbeeld drie keer een hoek die in radialen opgeslagen stond omgerekend in graden, voordat we deze printen. Omdat de hoek in radialen wél drie keer met een andere formule berekend wordt, is het niet makkelijk om dit in een loop te verwerken.
import math
# Dit programma print de hoek waar we naar op zoek zijn in graden.
# Driehoek 1:
# aanliggende zijde = 3,
# schuine zijde = 5.
#
# Wat is de hoek tussen aanliggende en schuine zijde?
a_1 = 3
s_1 = 5
hoek_radialen_1 = math.acos(a_1/s_1)
hoek_graden_1 = 180 * hoek_radialen_1 / math.pi
print('De hoek is:', hoek_graden_1, 'graden.\n')
# Driehoek 2:
# overstaande zijde = 3
# schuine zijde = 3 wortel(2).
#
# Wat is de hoek tussen aanliggende en schuine zijde?
o_2 = 3
s_2 = 3 * math.sqrt(2)
hoek_radialen_2 = math.asin(o_2/s_2)
hoek_graden_2 = 180 * hoek_radialen_2 / math.pi
print('De hoek is:', hoek_graden_2, 'graden.\n')
# Driehoek 3:
# overstaande zijde = 3,
# aanliggende zijde = 3.
#
# Wat is de hoek tussen aanliggende en schuine zijde?
o_3 = 3
a_3 = 3
hoek_radialen_3 = math.atan(o_3 / a_3)
hoek_graden_3 = 180 * hoek_radialen_3 / math.pi
print('De hoek is:', hoek_graden_3, 'graden.\n')
De hoek is: 53.13010235415598 graden.
De hoek is: 44.99999999999999 graden.
De hoek is: 45.0 graden.
Nu lijkt dit hier niet zo’n groot probleem - we hebben gewoon drie keer de formule \(\alpha_{graden} = \alpha_{rad} \cdot \frac{180^\circ}{\pi}\) geïmplementeerd. Als de problemen waarvoor je code gaat schrijven echter complexer worden, is het voor jezelf en voor je collega’s belangrijk om herhaalde stukjes identieke code te vermijden. We kunnen daar de volgende redenen voor geven:
Je code is makkelijker te onderhouden. Als er een foutje is dat je op moet lossen (of je hebt een betere manier om iets te implementeren gevonden), hoef je dat maar op één plek te doen.
Je code wordt korter, en daardoor beter te overzien.
Herhaalde blokjes identieke code kunnen vervangen worden door met functies te gaan werken. Een functie is een blokje code die niet uitgevoerd wordt wanneer Python van boven naar beneden door de code loopt, maar pas wanneer de functie aangeroepen wordt. Functies kunnen variabelen als inputs en outputs hebben, afhankelijk van wat we er mee willen. Het eerder genoemde script kunnen we dan ook omschrijven naar het onderstaande script, dat precies dezelfde werking heeft.
import math
# Dit programma print de hoek waar we naar op zoek zijn in graden.
# Functie die radialen omrekent naar graden
def radialen_naar_graden(hoek_radialen):
return 180 * hoek_radialen / math.pi
# Driehoek 1:
# aanliggende zijde = 3,
# schuine zijde = 5.
#
# Wat is de hoek tussen aanliggende en schuine zijde?
a_1 = 3
s_1 = 5
hoek_radialen_1 = math.acos(a_1/s_1)
print('De hoek is:', radialen_naar_graden(hoek_radialen_1), 'graden.\n')
# Driehoek 2:
# overstaande zijde = 3
# schuine zijde = 3 wortel(2).
#
# Wat is de hoek tussen aanliggende en schuine zijde?
o_2 = 3
s_2 = 3 * math.sqrt(2)
hoek_radialen_2 = math.asin(o_2/s_2)
print('De hoek is:', radialen_naar_graden(hoek_radialen_2), 'graden.\n')
# Driehoek 3:
# overstaande zijde = 3,
# aanliggende zijde = 3.
#
# Wat is de hoek tussen aanliggende en schuine zijde?
o_3 = 3
a_3 = 3
hoek_radialen_3 = math.atan(o_3 / a_3)
print('De hoek is:', radialen_naar_graden(hoek_radialen_3), 'graden.\n')
De hoek is: 53.13010235415598 graden.
De hoek is: 44.99999999999999 graden.
De hoek is: 45.0 graden.
Je bent al enkele functies tegengekomen zonder het misschien door te hebben. Bijvoorbeeld math.cos() in de eerste sessie was een functie die als input een hoek had en als output de cosinus van die hoek teruggaf. type() is ook een goed voorbeeld van een functie die als input een variabele neemt en het type van de variabele teruggeeft als output.
3.1.1. Syntax#
We definiëren een functie door middel van het def-sleutelwoord. Achter dit keyword zetten we de naam van de functie, gevolgd door de inputargumenten van de functie tussen ronde haken (()). We sluiten de regel daarna af met een dubbele punt om aan te geven dat in de volgende regels de inhoud van de functie volgt.
def mijn_functie(argument1, argument2, argument3):
Inputargumenten#
In de daarop volgende regels volgt inhoud van de functie. Deze zal waarschijnlijk iets doen met de inputargumenten. Je kunt binnen een functie een stukje Python-code typen dat de beoogde functionaliteit implementeert. Let op dat deze regels, net als bij een for-loop, ingesprongen moeten zijn met vier spaties ten opzichte van het def-keyword.
def tel_de_argumenten_op(argument_1, argument_2, argument_3):
som = argument_1 + argument_2 + argument_3
Outputargumenten#
Ten slotte moeten we nog de output van onze functie definiëren. Hiervoor gebruiken we een nieuw keyword: return. Achter dit keyword zetten wij alle objecten die de functie als output terug moet geven wanneer deze aangeroepen wordt, gescheiden door komma’s (,).
def product_en_som(argument_1, argument_2, argument_3):
product = argument_1 * argument_2 * argument_3
som = argument_1 + argument_2 + argument_3
return product, som
Wanneer je deze functie in je script-editor invoert en de code uitvoert zal je merken dat er niet veel gebeurt. Dat komt omdat deze functie nog helemaal niet uitgevoerd wordt. Dat kunnen we controleren door een print() (stiekem is dat trouwens ook een functie) toe te voegen aan onze functie:
def product_en_som(argument_1, argument_2, argument_3):
print('De inputargumenten zijn:', argument_1, argument_2, argument_3)
product = argument_1 * argument_2 * argument_3
som = argument_1 + argument_2 + argument_3
print(product)
print(som)
return product, som
Aanroepen/uitvoeren#
Ook nu worden de getallen product en som niet geprint.
Om een functie uit te voeren, en de berekening dus ook echt plaats te laten vinden, moet je na het definiëren van de functie deze aanroepen. Dat doen we door de naam van de functie in te typen, gevolgd door ronde haken met daartussen de waarden van de input argumenten die we willen gebruiken. Hieruit komt het resultaat van de functie terug, dus dit kunnen we opslaan in een variabele.
# Definieer de functie
def product_en_som(argument_1, argument_2, argument_3):
print('De inputargumenten zijn:', argument_1, argument_2, argument_3)
product = argument_1 * argument_2 * argument_3
som = argument_1 + argument_2 + argument_3
print('Het product is:', product)
print('De som is:', som, '\n')
return product, som
# Roep vervolgens de functie aan
resultaat = product_en_som(8, 2, -1)
resultaat = product_en_som(2, 2, 2)
resultaat = product_en_som(0, 1, -10)
print('Klaar. Het laatste resultaat:', resultaat)
De inputargumenten zijn: 8 2 -1
Het product is: -16
De som is: 9
De inputargumenten zijn: 2 2 2
Het product is: 8
De som is: 6
De inputargumenten zijn: 0 1 -10
Het product is: 0
De som is: -9
Klaar. Het laatste resultaat: (0, -9)
We zien dat de print-statements al worden uitgevoerd wanneer we de functie aanroepen, maar dat de output alleen opgeslagen is in de variabele resultaat. Deze is nog niet geprint. Dat kunnen we later wel doen met de print()-functie die we vorige week geleerd hebben:
print(resultaat)
(0, -9)
Je ziet dat dit resultaat bestaat uit twee getallen, precies zoals we zouden verwachten. Je kunt ook binnen het print-statement direct de functie aanroepen. Het resultaat wordt dan niet tussentijds in een variabele opgeslagen.
print(product_en_som(7, 3, -1))
De inputargumenten zijn: 7 3 -1
Het product is: -21
De som is: 9
(-21, 9)
3.1.2. Meerdere outputargumenten#
In het vorige blokje hebben we een functie behandeld die meerdere outputargumenten kende. We krijgen dan iets geks: de variabele resultaat bevatte twee getallen, opgeslagen in een soort lijst (tuple, lijkt erg veel op de eerder behandelde list).
def product_en_som(argument_1, argument_2, argument_3):
product = argument_1 * argument_2 * argument_3
som = argument_1 + argument_2 + argument_3
return product, som
resultaat = product_en_som(8, 2, -1)
print(type(resultaat))
print(resultaat[0])
print(resultaat[1])
print(resultaat)
<class 'tuple'>
-16
9
(-16, 9)
We kunnen de twee outputvariabelen (die nu dus samengepakt zitten in een tuple) ook netjes uitpakken, zodat we het product en de som netjes in individuele variabelen verder kunnen gebruiken.
resultaat_product, resultaat_som = product_en_som(8, 2, -1)
print(resultaat_product)
print(resultaat_som)
print(type(resultaat_product), type(resultaat_som))
-16
9
<class 'int'> <class 'int'>
3.1.3. Scripts met functies / Ontbinden in factoren#
Stel dat we het resultaat van \(ab + ac + ad\) willen bepalen met onze rekenmachine, en we weten dat het wel eens voorkomt dat we de som voor verschillende waarden van \(a\) willen bepalen. Dan loont het om de volgende ontbinding te gebruiken:
We kunnen op eenzelfde manier naar stukjes code kijken. Stel dat we de volgende code hebben, en we weten dat we in sommige toepassingen in plaats van graden de hoek als percentage van de hele cirkel willen printen.
import math
hoek_1 = (2/5) * math.pi
hoek_1_graden = hoek_1 * 180 / math.pi
print(hoek_graden)
hoek_2 = (5/6) * math.pi
hoek_2_graden = hoek_2 * 180 / math.pi
print(hoek_2_graden)
hoek_3 = (1/3) * math.pi
hoek_3_graden = hoek_3 * 180 / math.pi
print(hoek_3_graden)
180.0
150.00000000000003
59.99999999999999
Laten we dit eerst omschrijven, zodat de gemeenschappelijke deler (het omrekenen) in een functie staat.
import math
def reken_radialen_om(hoek_rad):
return hoek_rad * 180 / math.pi
hoek_1 = (1/4) * math.pi
print(reken_radialen_om(hoek_1))
hoek_2 = (1) * math.pi
print(reken_radialen_om(hoek_2))
hoek_3 = (1/3) * math.pi
print(reken_radialen_om(hoek_3))
45.0
180.0
59.99999999999999
Vervolgens kunnen we de functionaliteit eenvoudig aanpassen om een percentage van de hele cirkel te krijgen. We hoeven dit alleen in de omrekenfunctie aan te passen, en niet op alle plekken in de code waar een hoek in radialen gegeven staat.
import math
def reken_radialen_om(hoek_rad):
return hoek_rad * 100 / (2 * math.pi)
hoek_1 = (1/4) * math.pi
print(reken_radialen_om(hoek_1))
hoek_2 = (1) * math.pi
print(reken_radialen_om(hoek_2))
hoek_3 = (1/3) * math.pi
print(reken_radialen_om(hoek_3))
12.5
50.0
16.666666666666664
Het vervangen van stukjes identieke code door functies kun je vergelijken met ontbinden in factoren, in het Engels wordt het ook wel refactoring genoemd. Het kan verleidelijk zijn om stukjes code te kopieren-plakken wanneer je een programma schrijft. Het is dan verstandig om achteraf nog eens door je code heen te lopen om te kijken of er bepaalde stukjes code vaak voorkomen, en deze eruit te delen door een nieuwe functie te schrijven.