H3. Basisfuncties en modules

Uit noodzaak om al aan de slag te kunnen, hebben we enkele basisfuncties ontmoet, zoals print(), input() en type(). In dit hoofdstuk worden deze en meer functies in meer detail besproken. Pas later zien we hoe je je eigen functies kunt maken.

Elementen van elke functie

In het algemeen beschrijven we een functie als een blok herbruikbare code dat een bepaalde actie uitvoert. Zoals we variabelen vergeleken met gemarkeerde dozen, maken we een analogie tussen een functie en een grote machine. Bijvoorbeeld, een machine die pannenkoeken bakt. Het zal je niet verbazen dat die de veelbelovende naam ‘pannenkoek machine’ krijgt. Er zijn input sleuven aan de bovenkant, met labels melk, eieren, en meel. Dat zijn de functie argumenten. Je kunt beïnvloeden wat voor pannenkoeken de machine bakt door de juiste ingrediënten in de inputs te stoppen; bijvoorbeeld, als je volkoren pannenkoeken wilt, doe je volkoren meel in de meel input. Natuurlijk kunnen de zaken dramatisch fout lopen als je eieren in de melk input doet. Volgorde en structuur zijn uitermate belangrijk! Als de inputs gevuld zijn, begint de machine te ratelen. Je wacht geduldig naast de output sleuf die (tot niemand’s verrassing) het label return draagt. Na enige tijd verschijnt er een pannenkoek in de uitvoer. Die pannenkoek is de retourwaarde die de machine heeft geproduceerd. Eén van de aardige dingen van de pannenkoek machine is dat - zelfs als jij niet weet hoe je een pannenkoek moet bakken - je toch pannenkoeken kunt krijgen als je maar de juiste ingrediënten verstrekt. Dat is ook aardig aan functies: ze kunnen complexe zaken voor je regelen, zonder dat je hoeft te weten hoe ze dat doen. Je hoeft eigenlijk slechts drie dingen te weten:

1. Functie naam

Iedere functie heeft een naam. Net als variabele namen, mogen functie namen alleen bestaan uit letters, cijfers en underscores, en mogen ze niet starten met een cijfer. Vrijwel alle standaard Python functies bestaan alleen uit kleine letters. Gewoonlijk is de naam van een functie een korte beschrijving van wat de functie doet. Horend bij de naam van de functie is er steeds een openings- en sluithaakje. Die zijn er altijd. Sterker nog, het paar haakjes maakt voor de programmeur net duidelijk het onderscheid tussen functies en variabelen.

2. Functie argumenten

Sommige functies worden aangeroepen met argumenten. Argumenten worden geplaatst tussen de haakjes die achter de functienaam staan. Als er meerdere argumenten zijn, worden die door komma’s gescheiden. Als er geen argumenten zijn, dan blijven de haakjes leeg. De functie type() eist exact één argument. De functie print() laat een willekeurig aantal argumenten toe, telkens gescheiden met een komma. In het volgende voorbeeld worden drie argumenten gegeven, alledrie van het datatype string:

print( "Ik", "leer", "Python." )

Als een functie meerdere argumenten krijgt, is de volgorde cruciaal. Bijvoorbeeld, de standaard machtsfunctie pow() krijgt twee argumenten, integers of floats, en rekent de waarde uit van de eerste die wordt verheven tot de macht weergegeven door de tweede. (Eerder zagen we al de ** operator waarmee we machten kunnen berekenen. Willen we bijvoorbeeld $2^3$ berekenen, dan kunnen we kiezen: 2**3 of pow(2,3).) Let op, de namen die aan de variabelen zijn gegeven doen niet ter zake, de eerste wordt verheven tot de macht die de tweede is. Volgende twee prints geven daarom een andere waarde (dit louter illustratief, de variabele namen van de tweede print houden geen steek):

basis = 2
exponent = 3
print( pow( basis, exponent ) ) # basis wordt tot exponent verheven (resultaat is 8)
print( pow( exponent, basis ) ) # exponent wordt tot basis verheven (verwarrend!) (resultaat is 9)

3. Functie retourwaarde

Een functie heeft vaak een retour waarde. Als een functie een waarde retourneert, kun je die in je code gebruiken. Bijvoorbeeld, de int() functie retourneert een integer representatie van het argument dat is meegegeven. Je kunt deze retour waarde in een variabele stoppen middels een assignment, of je kunt de waarde op een andere manier gebruiken, bijvoorbeeld deze onmiddellijk printen. Je kunt er zelfs voor kiezen niks met de waarde te doen, maar in dat geval had het waarschijnlijk weinig zin om de functie aan te roepen. Voorspel even de uitvoer van volgende lijnen code:

x = 2.1
y = '3'
z = int( x )
print( z )
print( int( y ) )

Zoals je hierboven kunt zien, kun je zelfs een functie aanroep als argument meegeven aan een (andere) functie, bijvoorbeeld, in de laatste regel van de code hierboven krijgt de print() functie als argument een aanroep van de int() functie mee. De aanroep van int() vindt dan plaats voordat de print() wordt afgehandeld, dus de return waarde van int() is een argument voor print(). We lezen hier dus van binnen naar buiten: y als argument voor int() als argument voor print().

Ingebouwde functies

We bekijken in meer detail enkele ingebouwde functies in Python. Deze lijst is niet volledig. Wil je toch een volledige lijst? Dan vind je die terug op deze documentatie webpagina1.

Merk op, om sep en end te gebruiken in de print() functie, moet je “keyword” argumenten opnemen. Hier is niet de juiste volgorde van belang zoals gewone argumenten, maar gebruik je keywords om te verduidelijken welk argument volgt na het gelijkheidsteken. Keyword argumenten komen altijd na alle gewone argumenten. Heel vaak zijn keyword argumenten optioneel, worden ze niet expliciet meegegeven, dan is er altijd een standaard (En: default). Bij het keyword argument sep is de default een spatie, bij het keyword argument end is de default een nieuwe lijn.

Modules

Python biedt basis functies, waarvan een aantal hierboven besproken. Helaas zijn echter niet alle functies rechtstreeks toegankelijk. Zo bestaat er geen vierkantswortel() functie in python (Terzijde: de vierkantswortel functie mag dan misschien niet bestaan, we kunnen natuurlijk wel de pow() functie met \(\frac{1}{2}\) als tweede argument gebruiken, want \(\sqrt{x} = x^{\frac{1}{2}}\)). Toch kunnen we heel eenvoudig een vierkantswortelfunctie aanroepen, als we weten waar die ‘verstopt’ zit. Nee - Python speelt niet graag verstoppertje, maar ‘verstopt’ functies om het bos door de bomen te blijven zien. Er zijn namelijk vele duizenden functies beschikbaar. Met uitzondering van sommige basis ingebouwde functies zoals de voorbeelden hierboven, zitten alle beschikbare functies mooi geordend in modules. Alle machines (functies) zitten als het ware in lades (modules) van een grote kast in de garage (RAM geheugen). Onze pannenkoekenfunctie bijvoorbeeld kunnen we terugvinden in de ‘dessert’-lade. Als we die lade openen, zullen we daar misschien ook de wafelsfunctie en chocomoussefunctie terugvinden.

Om de functies van een module te gebruiken in jouw programma, moet je de juiste module importeren (lade openen), waarna je toegang krijgt tot alle functies binnen de geïmporteerde module. Importeren en functies uit modules gebruiken doe je zo:

import math
a = math.sqrt( 9 )
print( a )

Hierbij vallen enkele belangrijke syntax regels meteen op. Het importeren van de module (hier math, een lade vol wiskundige functies) vindt altijd plaats bovenaan het stuk code, ongeacht van de lengte van de code. Al schrijven we een script met duizend lijnen en hebben we een functie uit de math module pas op het einde nodig, dan nog wordt math reeds van bij het begin geïmporteerd. Om een functie uit de math module te gebruiken, moeten we Python steeds duidelijk maken waar we de functie vandaan halen. Dit gebeurt door middel van een punt tussen de modulenaam en de functienaam: math.sqrt(). (Merk op: sqrt is een engelstalige afkorting voor square root, wat vierkantswortel betekent.) Het punt wordt in Python dus niet enkel als decimaalteken gebruikt, het is ook voorbehouden voor functie aanroepen vanuit modules. Verder is het gebruik van ingebouwde functies en module functies hetzelfde. De modulenaam.functienaam() wordt altijd gevolgd door een paar haakjes, binnen dewelke argumenten worden opgelijst. Al is er geen enkel argument, dan nog zijn de haakjes verplicht. Meerdere argumenten binnen de haakjes worden gescheiden door komma’s. De volgorde van argumenten is cruciaal, tenzij het over keyword argumenten gaat. De retourwaarde van de functie kunnen we opslaan in een variabele (in het bovenstaande voorbeeld eenvoudigweg a genoemd), of rechtstreeks gebruiken.

Soms hebben modules vrij lange namen. Dat zou betekenen dat we telkens wanneer we een functie uit die module willen gebruiken, we de hele modulenaam.functienaam() moeten typen, wat op den duur wel tijd kost. We kunnen dit vermijden door een afkorting (of “alias”) voor de module te bedenken, en die door middel van het gereserveerde Pythonwoord as in te voeren. De afkortingsnaam kunnen we in principe zelf kiezen. Toch worden vele modules quasi altijd met dezelfde naam afgekort. Zo vaak zelfs dat vele programmeurs wel de afkorting, maar niet de échte modulenaam onthouden. Twee modules die we in de toekomst nog vaak zullen ontmoeten zijn numpy en matplotlib.pyplot, gebruikt voor respectievelijk numerische berekeningen en grafische voorstellingen en figuren. We kunnen die als volgt importeren:

import numpy as np
import matplotlib.pyplot as plt
# daarna kunnen we de functies van deze modules als volgt gebruiken :
np.mean()    # voorbeeld, voorlopig nog zonder argumenten
plt.imshow() # voorbeeld, voorlopig nog zonder argumenten

Je mag er van uitgaan dat voor ieder min-of-meer-algemeen probleem dat je wilt oplossen, er iemand is geweest die er een module voor ontwikkeld heeft met functies die het eenvoudig of zelfs triviaal maken om het probleem op te lossen. Dus in de praktijk geldt: ga niet meteen alles zelf coderen, maar zoek eerst even uit of je niet gebruik kunt maken van de moeite die iemand anders gedaan heeft!