Program Arcade Games
With Python And Pygame

Chapter 9: Functies

9.1 Inleiding tot functies

Video: Waarom gebruikene we fucnties?

Functies worden gebruikt om twee redenen. Een, om de code makkelijker en leesbaarder te maken. Twee, om code te gaan herbruiken.

Beeld je de code in die een speelgoedwagen bestuurt. Indien de programmeur wenst dat de auto kan vooruit rijden zou hij devolgende commando's kunnen gebruiken :

turnOnMotor()
pauseOneSecond()
turnOffMotor()

Bij het definieren van functies, wordt het programma veel leesbaarder en is het makkelijker om code te gaan hergebruiken.

def moveForward():
    turnOnMotor()
    pauseOneSecond()
    turnOffMotor()

Door zichzelf zal deze code er niet voor zorgen dat de auto vooruit rijdt. Het zegt tegen de computer hoe hij vooruit moet rijden. Daarvoor moet je een functie met de eigenlijke code in oproepen :

moveForward()

Wanneer je alle functies hebt gedefinieerd dat een auto zou kunnen, zou de code van het uiteindelijke programma er als volgt kunnen uitzien :

moveForward()
turnLeft()
moveForward()
moveForward()
turnRight()
moveForward()

Denk eraan dat je moveForward drie keer voorkomt in de code. telkens wordt er een heleboel code opgeroepen. Door gebruik te maken van functies hoef je niet telkens al deze code te herhalen en wordt je programma veel kleiner en overzichtelijker.

Functie namen zijn zeer belangrijk. Wanneer je duidelijke functie namen kiest wordt het zelfs voor niet-programmeurs mogelijk om de code te lezen en om een idee te krijgen van wat er gebeurt. Functienamen volgen dezelfde regels als namen van variabelen en moeten eveneens starten met een kleine letter.

9.2 Functie Parameters

fig.function

Functies kunnen parameters bevatten. Ze kunnen gebruikt worden om de flexibiliteit van een functie te verhogen door andere dingen te doen afhankelijk van de parameters die worden doorgegeven. Bijvoorbeeld : Een functie met de naam moveForward() kan gebruikt worden om een robot gedurende 1 seconde voort te bewegen. De functie zou door het gebruik van een parameter kunnen meegeven hoeveel seconden de robot moet voortbewegen. Bijvoorbeeld moveForward(3) zou de robot dan 3 seconden voort bewegen en moveForward(6) zou de robot 6 seconden voort bewegen.

De aangepaste functie voor de robot zou er dan als volgt kunnen uitzien :

def moveForward(time):
    turnOnMotor()
    for i in range(time):
        pauseOneSecond()
    turnOffMotor()

Hier is een andere functie die kan worden uitgevoerd zonder de robot auto. Deze functie berekent en print het volume van een bol :

def volumeSphere(radius):
    pi = 3.141592653589
    volume = (4 / 3) * pi * radius ** 3
    print("Het volume is", volume)
Video: Maak een Function
Parameters worden toegewezen wanneer de functie wordt opgeroepen en niet wanneer deze wordt gedefinieerd.

De naam van de functie is volumeSphere. De data die in de functie binnenkomt wordt bewaard in radius. Het resultaat van het volume wordt geprint op het scherm. De variabele radius krijgt geen waarde hier. Regelmatig zijn nieuwe programmeurs verward, omdat parametervariabelen geen waarde krijgen wanneer de functie wordt bepaald, maar wanneer de functie wordt opgeroepen.

Om de functie op te roepen gebruik je :

volumeSphere(22)

De variabale radius in de functie wordt gecreerd en geinitieerd met de waarde 22. De code van de functie wordt pas uitgevoerd wanneer de functie wordt opgeroepen.

Wat moeten wenu doen indien we meer dan 1 waarde willen doorgeven? Meerdere parameters kunnen worden doorgegeven door de parameters te scheiden met een komma :

def volumeCylinder(radius, height):
    pi = 3.141592653589
    volume = pi * radius ** 2 * height
    print("Het volume is", volume)

De functie kan als volgt worden opgeroepen :

volumeCylinder(12, 3)

Parameters worden in volgorde bepaald, dus radius zal de waard 12 krijgen en height de waarde 3.

Jammergenoeg zijn deze voorbeelden van functies gelimiteerd. Waarom? Wanneer een persoon wil gebruik maken van de functie volumeCylinder voor het berekenen van 6 flesjes zal het niet lukken omdat slechts het volume van 1 flesje zal worden weergegeven. Is het dan niet mogelijk om de functie te gebruiken om het volume van 1 flesje te berekenen en dat dan te vermenigvuldigen met 6?

Dit kan opgelost worden met een return statement. Bijvoorbeeld :

def volumeCylinder(radius, height):
    pi = 3.141592653589
    volume = pi * radius ** 2 * height
    return volume

Return is geen functie, en moet daarom ook niet tussen haakjes. Dus schrijf niet return(volume).

Omwille van de return, kan je de functie later gebruiken als deel van de vermenigvuldiging om de 6 flesjes te berekenen. :

sixPackVolume = volumeCylinder(2.5, 5) * 6

De waarde die teruggegeven wordt vanuit volumeCylinder wordt gebruikt om te vermenigvuldigen met zes.

Er is een groot verschil tussen een functie dat zijn waarde prints en een functie dat zijn waarde teruggeeft. Kijk naar de onderstaande code en probeer het uit.

# Functie print het resultaat
def sumPrint(a, b):
    result = a + b
    print(result)

# Functie geeft het resultaat terug
def sumReturn(a, b):
    result = a+b
    return result

# Dit print de som van 4+4
sumPrint(4, 4)

# Hier gebeurt dit niet
sumReturn(4, 4)

# Hier wordt de som niet weergegeven in x1
# Dit geeft de waarde 'None' terug
x1 = sumPrint(4, 4)

# Hier wel
x2 = sumReturn(4, 4)

9.3 Documenteren van functies

Functies in Python hebben typisch commentaar in hun eerste statement van de functie staan. Deze commentaart is afgebakend met drie dubbele quotes en wordt de docstring genoemd. Een functie kan er als volgt uitzien :

def volumeCylinder(radius, height):
    """Returns het volume van een cylinder waneer straal en hoogte wordt megegeven."""
    pi = 3.141592653589
    volume = pi * radius ** 2 * height
    return volume

Het leuke aan het gebruik van docstrings in functies is dat de commentaar kan worden opgehaad en gebruikt worden in website als documentatie voor je coding door gebruik te maken van een tool zoals Sphinx. De meeste talen hebben gelijkaardige tools die kunnen helpen om het documenteren te vergemakkelijken.Dit kan je heel veel tijd besparen wanneer je start met groter programma's.

9.4 Variabele Scope

Video: Variabele Scope

Het gebruik van functies introduceert ook meteen het concept van scope of bereik. De scope is wanneer de code van de varaibele "werkt" en kan gebruikt worden. Kijk bevoorbeeld hieronder :

# Definieer een simpele functie 
# x is gelijk aan 22
def f():
    x = 22

# Roep de functie 
f()
# Dit mislukt, x bestaat alleen in f()
print (x)

De laatste lijn zal een fout genereren omdat x niet bestaat buiten de f() functie. De variabele wordt gecreeerd wanneer f() wordt opgeroepen en het geheugen dat gebruikt wordt, wordt vrijgegeven van zodra f() eindigt.

Een meer verwarrende regel is de toegang tot variabelen die gecreerd zijn buiten de f() functie. In de volgende code wordt x gecreerd voor de f() functie en dan deze dus gelezen worden in de f() functie.

# Maak de variabele x en zet ze op 44
x = 44

# Definieer een simpele functie dat x print
def f():
    print(x)

# Roep de functie op
f()

Variabelen die gecreerd zijn voor de functie mogen gelezen worden binnen de functie wanneer deze door de functie niet worden gewijzigd. De code die heel gelijkaardig is aan de bovenstaande code zal falen. De computer zal zeggen dat hij niet weet wat x is.

# Creer de variabele x en zet ze op 44
x=44

# Definieer een simpele functie dat x afprint
def f():
    x += 1
    print(x)

# Roep de functie op
f()

Andere talen hebben meer complexe regels rond het creeren van variabelen en hun bereik. Python gelukkig niet en is op die manier een goede taal om te beginnen programmeren.

9.5 Pass-by-copy

Functies geven hun waarde door door een copie te maken van het origineel. Voorbeeld :

# Definieer een simpele functie dat x afprint
def f(x):
    x += 1
    print(x)

# defineer y
y = 10
# Roep de functie op
f(y)
# Print y om te zien wat er is gebeurt
print(y)

De waarde van y is niet gewijzigd, alhoewel de f() functie de waard heeft doorgegeven. Elke variabele dat gebruikt wordt als een parameter is een volledig nieuwe variabele. De waarde van de variabele werd gecopieerd van waar ze werd opgeroepen.

Dit was begrijpbaar, niet? Het wordt wel verwarrend wanneer de variabelen dezelfde naam hebben in de functie en waar de functie wordt opgeroepen. De code is identiek als de bovenstaande, behalve dat we y vervangen hebben door x.

# Definier een simpele functie dat x afprint
def f(x):
    x += 1
    print(x)

# Defineer x
x = 10
# Roep de functie op
f(x)
# Print x om te zien wat er is gebeurt
print(x)

De output is dezelfde dan met de variabele y. Eigenlijk gaat het hier over twee verschillende x variabelen. Er is een x variabele die bestaat binnenin de functie en er is een andere x variabele die bestaat buiten de functie.

9.6 Functions Calling Functions

Het is ook mogelijk dat een functie een andere functie oproept. Voorbeeld :

def armOut(whichArm, palmUpOrDown):
	# code komt hier
	
def handGrab(hand, arm):
	# code kont hier

Dan kan er een andere functie worden gecreerd die deze functies oproept :

def macarena():
	armOut("right", "down")
	armOut("left", "down")
	armOut("right", "up")
	armOut("left", "up")
	handGrab("right", "left arm")
	handGrab("left", "right arm")
	# etc

9.7 Main Functie en globale variabelen

Wanneer programma's groot worden is het belangrijk om de code te organiseren en ze te verdelen in functies. Python laat ons de keuze om een programma te schrijven zonder functies en dus alles bij elkaar te steken, of om een programma onder te verdelen in duidelijk afgebakende functues.

Het is alsof je al je kleren in het midden van je kamer laat liggen in plaats van ze mooi op te bergen op de voorziene plaats in de kasten. Van zodra je veel kleren hebt is het veel makkelijker om deze overzichtelijk te hebben in je kasten.

Al je code en al je variabelen zouden zich moeten bevinden in functies. Dit zorgt ervoor dat je code overzichtelijk blijft. Het zal je eveneens helpen bij het zoeken naar bugs in je programma. De variabelen die gecreerd worden op “level 0” noemen we de globale variabelen. Globale variabelen zijn een zeer slechte zaak. Waarom? Omdat elk stukje code gelijk waar in je programma deze variabelen kunnen wijzigen. Wanneer je daarnaast de variabelen in functies plaatst, kan alleen de code van die functie je variabele beinvloeden. Stel dat je programma 50.000 lijnen omvat en je functie slechts 50 dan zal het veel makkelijker zijn om binnen de functie te zoeken naar de oorzaak van een probleem.

Een betere manier om Python te schrijven is door gebruik te maken van het volgende patroon :

def main():
	print("Hello world.")
	
main()

In dit geval bevindt al de code die normaal op “level 0” zit, zich in de main functie. De laatste lijn van het bestand roept dan de main functie op.

Maar wacht! Er moet ook een ander probleem worden opgelost. In Hoofdstuk 15 zullen we het hebben over het onderverdelen van ons programma in meerdere bestanden. We kunnen dan van het import commando gaan gebruik maken. Wanneer we het import commando gebruiken met deze module, dan zal automatisch de main function starten en dat willen niet.

Om dit op te lossen kunnen we ons programma laten checken op een gloabel variabele dat automatisch gedefinieerd wordt door python. (Ok, ik weet dat ik net zei dat gloabel variabelen een zeer slechte zaak zijn ;-( De variabele heet __name__, met twee onderscores voor en achter. We kunnen deze gebruiken om te checken of de code geimporteerd werd of draait. Wanneer de code draait zal Python automatisch de variabele zetten op __main__. In het andere geval wordt de code slechts gedefinieerdin de main functie. De code kan dan worden opgeroepen wanneer het nodig is.

Dit is hoe de Python code er zou kunnen uitzien :

def main():
	print("Hello world.")
	
if __name__ == "__main__":
    main()

Een van de redenen waarom ik hou van Python als startende taal is dat je dergelijke complexiteit slechts hoeft te gebruiken als je ze nodig hebt. Met andere talen zoals Java heb je dit zelfs nodig voor de kleinste programma's.

Om de zaken makkelijker te maken in dit boek maken we geen gebruik van deze patronen. Maar wanneer je achteraf meer complexe programma's gaat maken is het noodzakelijk dat je al je kledij niet op een hoop gooit, bij manier van spreken.

Wanneer je een super enthousiaste programmeur bent, probeer je maar meteen je code op die manier te schrijven. In het begin is het een beetje meer uitdagend, maar later zal het makkelijker zijn om complexe programma's te beheren.

Hieronder volgt een voorbeeld van hoe er een basis Pygame template kan uitzien gebruik makend van dit patroon :
pygame_base_template_proper.py

Voor het ogenblik is het gebruik van dergelijke patronen niet verplicht. Ik kan er mee leven dat de kleren op een hoopje worden gegooid. Ik ben al blij dat jullie kleren aan hebben ;-) (Voor de freaks onder ons. We kunnen onze programma's nog verder opschonen van zodra we belanden bij “Klasses.”

9.8 Herhaling

9.8.1 Multiple Choice Kwis

Click here for a multiple-choice quiz.

9.8.2 Korte Inhoud Werkblad

Click here for the chapter worksheet.

9.8.3 Oefening

Click here for the chapter lab.


You are not logged in. Log in here and track your progress.