Previous Page

Up One Level

Next Page

Python Tutorial

Contents

Index

Vorige: 3. Een informele inleiding in Python Omhoog: Python Tutorial Volgende: 5. Datastructuren
Subsections
  • 4.1 if-statements
  • 4.2 for-statements
  • 4.3 De functie range()
  • 4.4 break- en continue-statements, en else-clauses in loops
  • 4.5 pass-statements
  • 4.6 Het definiëren van functies
  • 4.7 Meer over het definiëren van functies
    • 4.7.1 Default argumentwaarden
    • 4.7.2 Keyword-argumenten
    • 4.7.3 Een willekeurig aantal argumenten
    • 4.7.4 Argumentlists unpacken
    • 4.7.5 Lambda forms
    • 4.7.6 Documentatiestrings


4. Meer over control flow

Naast het while-statement dat we net hebben geïntroduceerd, beschikt Python over de gebruikelijke control flow statements die we ook uit andere talen kennen, hier en daar met een eigen Python-tintje.


4.1 if-statements

Het if-statement is misschien wel het bekendste soort statement dat er is. Bijvoorbeeld:

>>> x = int(raw_input("Voer een integer in: "))
>>> if x < 0:
...      x = 0
...      print 'Negatief getal omgezet naar nul'
... elif x == 0:
...      print 'Nul'
... elif x == 1:
...      print 'Enkel'
... else:
...      print 'Meer'
...

Er kunnen nul of meer elif's in het statement voorkomen; de else is optioneel. Het keyword `elif' is een samentrekking van `else if', en kun je gebruiken om het inspringen binnen de perken te houden. De opeenvolging van if ... elif ... elif ... is een manier om de switch- of case-statements te emuleren die in andere talen voorhanden zijn.


4.2 for-statements

Het for-statement van Python wijkt licht af van wat je wellicht gewend bent uit C of Pascal. In plaats van over een rekenkundige opeenvolging van getallen te itereren (zoals in Pascal), of de gebruiker de mogelijkheid te geven om zowel de iteratiestap als de stopconditie te definiëren (zoals in C), itereert het for-statement in Python over de items in een sequence (een lijst of een string), in de volgorde waarin ze in de sequence voorkomen. Bijvoorbeeld:

>>> # Geef de lengte van de volgende strings:
... a = ['kat', 'raam', 'defenestrate']
>>> for x in a:
...     print x, len(x)
... 
kat 3
raam 4
defenestrate 12

Het is niet veilig om de sequence waarover geïtereerd wordt, binnen de lus te wijzigen (dit kan alleen voorkomen bij sequences die gewijzigd kunnen worden, zoals lists). Als je de list waarover geïtereerd wordt, moet aanpassen (bijvoorbeeld om geselecteerde items te dupliceren) moet je de iteratie uitvoeren over een kopie van de list. De slicenotatie komt hierbij van pas:

>>> for x in a[:]: # maak met behulp van slicing een kopie van de complete lijst
...    if len(x) > 6: a.insert(0, x)
... 
>>> a
['defenestrate', 'kat', 'raam', 'defenestrate']


4.3 De functie range()

Als je toch moet itereren over een sequence van getallen, komt de ingebouwde functie range() goed van pas. Deze functie genereert lists op basis van rekenkundige reeksen:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Het opgegeven eindpunt maakt nooit deel uit van de gegenereerde list; range(10) genereert een list van 10 waarden, die exact overeenkomen met de toegestane indices voor een sequence van 10 items lang. Het is ook mogelijk om de reeks te laten beginnen bij een ander getal, of om een afwijkend increment te specificeren (dit mag zelfs negatief zijn). Het increment wordt soms “stap” genoemd):

>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(-10, -100, -30)
[-10, -40, -70]

Om te itereren over de indices van een sequence, kun je range() en len() als volgt combineren:

>>> a = ['Iene', 'miene', 'mutte']
>>> for i in range(len(a)):
...     print i, a[i]
... 
0 Iene
1 miene
2 mutte


4.4 break- en continue-statements, en else-clauses in loops

Het break-statement breekt uit de binnenste for-of while-loop (lus) waarin hij voorkomt, net als in C .

Het continue-statement, dat eveneens uit C geleend is, gaat verder met de volgende iteratie van de loop.

Loopstatements kunnen een else-clause hebben; deze wordt uitgevoerd wanneer de loop beëindigd wordt omdat de list waarover geïtereerd wordt, uitgeput raakt (bij een for) of als de conditie onwaar wordt (bij while), maar niet als de loop onderbroken wordt door een break statement. Dit wordt verduidelijkt aan de hand van de volgende loop, waarbinnen gezocht wordt naar priemgetallen:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print n, 'is gelijk aan', x, '*', n/x
...             break
...     else:
...         # de loop is beëindigd zonder een factor te vinden
...         print n, 'is een priemgetal'
... 
2 is een priemgetal
3 is een priemgetal
4 is gelijk aan 2 * 2
5 is een priemgetal 
6 is gelijk aan 2 * 3
7 is een priemgetal 
8 is gelijk aan 2 * 4
9 is gelijk aan 3 * 3


4.5 pass-statements

Het pass-statement doet niets. Het is bedoeld voor gebruik op plaatsen waar syntactisch gezien een statement vereist wordt, terwijl in het programma geen actie nodig is. Bijvoorbeeld:

>>> while True:
...       pass # Bezig - wacht op keyboard interrupt
...


4.6 Het definiëren van functies

We kunnen een functie schrijven die de Fibonaccireeks tot aan een willekeurige grens afdrukt:

>>> def fib(n):    # druk Fibonaccireeks af tot aan n
...     """Druk een Fibonaccireeks af tot aan n."""
...     a, b = 0, 1
...     while b < n:
...         print b,
...         a, b = b, a+b
... 
>>> # Roep nu de zojuist gedefinieerde functie aan:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

Met het keyword def wordt een functiedefinitie gestart. Het keyword moet gevolgd worden door de naam en de lijst met formele parameters van de functie. De parameterlijst moet tussen haakjes staan. Op de volgende regel begint de body van de functie; deze moet ingesprongen zijn. Het eerste statement van de functiebody mag een string literal zijn; in dat geval vormt deze string literal de documentatiestring of docstring van de functie..

Er zijn tools beschikbaar die op basis van docstrings automatisch elektronische of geprinte documentatie genereren, of die de gebruiker interactief door code laten browsen. Het is een goede gewoonte om docstrings op te nemen in alle code die je schrijft.

Door een functie uit te voeren wordt een nieuwe symbol table geïntroduceerd die gebruikt wordt voor de lokale variabelen van de functie. Om precies te zijn: alle assignments aan variabelen binnen de functie slaan de toegekende waarden op in de lokale symbol table, terwijl bij referenties aan variabelen eerst in de lokale symbol table gezocht wordt, dan in de globale, en tenslotte in de tabel met ingebouwde namen. Je kunt dus niet rechtstreeks een waarde toekennen aan globale variabelen (tenzij deze expliciet genoemd worden in een global-statement), al kun je er wel aan refereren.

De concrete parameters (argumenten) van een functieaanroep worden in de lokale symbol table van de betreffende functie toegevoegd wanneer deze wordt aangeroepen. Argumenten worden doorgegeven met behulp van “call by value” (waarbij de value, dus de waarde altijd een referentie naar een object is, en niet de waarde van dat object) 4.1 . Wanneer een functie een andere functie aanroept, wordt voor die aanroep een nieuwe lokale symbol table aangemaakt.

Een functiedefinitie voegt de naam van de functie toe in de huidige symbol table. De waarde van de functienaam heeft een type dat door de interpreter herkend wordt als een user-defined functie. Deze waarde kan weer toegekend worden aan een andere naam, die daardoor ook als functie gebruikt kan worden. Dit kan gebruikt worden als een algemeen mechanisme om zaken te hernoemen:

>>> fib
<function object at 10042ed0>
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

Je zou kunnen tegenwerpen dat fib geen functie is, maar een procedure. In Python zijn procedures gewoon functies die geen waarde teruggeven, net als in C. Om precies te zijn: technisch gesproken geven procedures wel degelijk een waarde terug, zij het dat deze vrij oninteressant is. Deze waarde heet None (dit is een ingebouwde naam). Het afdrukken van de waarde None wordt normaal gesproken onderdrukt door de interpreter in die gevallen waar het de enige waarde is die zou worden afgedrukt. Als het echt nodig is, kun je hem wel zichtbaar maken:

>>> print fib(0)
None

Het is niet moeilijk om een functie te schrijven die een lijst van getallen uit de Fibonaccireeks teruggeeft, in plaats van ze af te drukken:

>>> def fib2(n): # geef Fibonaccireeks tot aan n terug 
...     """Geef de Fibonaccireeks tot aan n als list terug."""
...     result = []
...     a, b = 0, 1
...     while b < n:
...         result.append(b)    # zie onder 
...         a, b = b, a+b
...     return result
... 
>>> f100 = fib2(100)    # roep de functie aan
>>> f100                # druk het resultaat af
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Dit voorbeeld laat weer een aantal nieuwe Pythonkenmerken zien:

  • Het return-statement komt terug met een waarde uit een functie. return zonder expressie-argument geeft None terug. Als je door het einde van een procedure heenvalt, wordt eveneens None teruggegeven.

  • Het statement result.append(b) roept een method van het listobject result aan. Een method is een functie die “hoort bij” een object, en heet obj.methodname; daarbij is obj de naam van een object (dit kan een expressie zijn), en methodname is de naam van een method die gedefinieerd wordt door het type van het object. Verschillende types definiëren verschillende methods. Methods van verschillende types kunnen dezelfde naam hebben, zonder dat dit dubbelzinnigheid in de hand werkt. (Het is mogelijk om je eigen objecttypes en methods te definiëren, met behulp van classes; we zullen hier verderop in deze tutorial op terugkomen.) De method append() die gebruikt wordt in het voorbeeld, is gedefinieerd voor listobjecten; hij voegt een nieuw element toe aan het einde van de list. In dit voorbeeld is dit hetzelfde als "result = result + [b]", maar dan efficiënter.


4.7 Meer over het definiëren van functies

Het is ook mogelijk om functies met een variabel aantal argumenten te definiëren. Dit kan op drie manieren, die in combinatie met elkaar gebruikt kunnen worden.


4.7.1 Default argumentwaarden

De meest effectieve vorm is het specificeren van een defaultwaarde voor één of meerdere argumenten. Hiermee wordt een functie gecreëerd die aangeroepen kan worden met minder argumenten dan gedefinieerd in zijn signature. Bijvoorbeeld:

def vraag_ok(prompt, opnieuw=4, klacht='Ja of nee, astublieft!'):
    while True:
        ok = raw_input(prompt)
        if ok in ('j', 'ja'): return True
        if ok in ('n', 'ne', 'nee'): return False
        opnieuw = opnieuw - 1
        if opnieuw < 0: raise IOError, 'gebruiker werkt niet mee'
        print klacht

Deze functie kan aangeroepen worden als: vraag_ok('Wilt u stoppen'), maar ook als: vraag_ok('Bestand overschrijven?', 2).

In dit voorbeeld wordt ook voor het eerst het keyword in gebruikt. Hiermee test je of een sequence een bepaalde waarde bevat.

De defaultwaarden worden geëvalueerd wanneer de functiedefinitie geëvalueerd wordt in de scope waarbinnen hij gedefinieerd is, dus de uitvoer van

i = 5

def f(arg=i):
    print arg

i = 6
f()

is 5.

Belangrijk: De defaultwaarde wordt slechts eenmaal geëvalueerd. Dit is van belang als de default een mutable object is, bijvoorbeeld een list, dictionary, of een instance van een class. De volgende functie voegt bijvoorbeeld de argumenten van opeenvolgende calls toe aan de list die de defaultwaarde vormt:

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

De uitvoer hiervan ziet eruit als:

[1]
[1, 2]
[1, 2, 3]

Als het niet de bedoeling is om de defaultwaarde voor iedere call te hergebruiken, kun je de functie schrijven als:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L


4.7.2 Keyword-argumenten

Functies kunnen ook aangeroepen worden met keyword-argumenten in de vorm "keyword=value". Zo kan de volgende functie:

def papegaai(voltage, toestand='koud', actie='zoemen', type='Grijze Roodstaart'):
    print "-- Deze papegaai gaat niet ", actie,
    print "als je er", voltage, "volt op zet."
    print "-- Mooi verenpak, zo'n ", type
    print "-- Hij is ", toestand, "!"

op de volgende manieren aangeroepen worden:

papegaai(1000)
papegaai(actie = 'zoemen', voltage = 1000000)
papegaai('duizend', toestand = 'dood')
papegaai('een miljoen', 'van het leven beroofd', 'springen')

maar de volgende aanroepen zijn allemaal incorrect:

papegaai()                     # verplicht argument niet meegegeven
papegaai(voltage=5.0, 'dood')  # keyword argument gevolgd door niet-keyword argument
papegaai(110, voltage=220)     # twee waarden meegegeven voor argument
papegaai(acteur='John Cleese') # onbekend keyword

In de argumentenlijst van een functieaanroep worden de positional arguments (dit zijn argumenten die geïdentificeerd worden door hun positie in de argument list) gevolgd door keyword arguments, als die er zijn; de gebruikte keywords moeten voorkomen in de lijst van formele parameters. Daarbij maakt het niet uit of een formele parameter een defaultwaarde heeft of niet. Argumenten mogen niet meer dan één waarde toegekend krijgen – formele parameters die al als positional arguments voorkomen, mogen niet in dezelfde aanroep voorkomen als keyword arguments. De functie-aanroep in het volgende voorbeeld voldoet niet aan deze restrictie, en loopt daarom fout:

>>> def function(a):
...     pass
... 
>>> function(0, a=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'

Als de laatste formele parameter de vorm “**naam” heeft, wordt hierin een dictionary meegegeven met alle keyword arguments waarvan het keyword niet correspondeert met een formele parameter. Deze kan voorkomen in combinatie met een formele parameter van de vorm “*naam” (deze wordt in de volgende paragraaf beschreven) waaraan een tuple meegegeven wordt die alle positionele argumenten bevat die niet corresponderen met de lijst van formele parameters. (*naam moet voor **name staan.) Als we bijvoorbeeld de volgende functie definiëren:

def cheeseshop(kind, *arguments, **keywords):
    print "-- Do you have any", kind, '?'
    print "-- I'm sorry, we're all out of", kind
    for arg in arguments: print arg
    print '-'*40
    keys = keywords.keys()
    keys.sort()
    for kw in keys: print kw, ':', keywords[kw]

Deze kan als volgt aangeroepen worden:

cheeseshop('Limburger', "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           client='John Cleese',
           shopkeeper='Michael Palin',
           sketch='Cheese Shop Sketch')

en geeft dan natuurlijk als uitvoer:

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

Merk op dat de sort()-method van de lijst met keyword arguments aangeroepen wordt voordat de inhoud van de dictionary keywords afgedrukt wordt; gebeurt dit niet, dan worden de argumenten in willekeurige volgorde afgedrukt.


4.7.3 Een willekeurig aantal argumenten

Tenslotte de minst gebruikelijke manier: je kunt opgeven dat een functie aangeroepen kan worden met een willekeurig aantal argumenten. “Willekeurig” betekent in dit verband “vooraf onbepaald”. Bij de aanroep worden de meegegeven argumenten verpakt in een tuple. Vóór de variabele argumenten mogen nul of meer normale argumenten voorkomen.

def fprintf(file, format, *args):
    file.write(format % args)


4.7.4 Argumentlists unpacken

De omgekeerde situatie doet zich voor wanneer de argumenten al in een list of tuple staan, maar uitgepakt moeten worden om ze mee te geven aan een functie die afzonderlijke positionele argumenten vereist. De ingebouwde functie range(), bijvoorbeeld, verwacht afzonderlijke start en stop argumenten. Als deze niet afzonderlijk beschikbaar zijn, kun je de functieaanroep coderen met de *-operator om de argumenten uit een list of tuple te unpacken:

>>> range(3, 6)             # normale aanroep met afzonderlijke argumenten
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args)            # aanroep met argumenten die uit een list gehaald moeten worden
[3, 4, 5]


4.7.5 Lambda forms

Op veler verzoek zijn enkele taalkenmerken die bekend zijn uit functionele programmeertalen zoals Lisp, overgenomen in Python. Met het keyword lambda kunnen kleine, naamloze functies gemaakt worden. De volgende functie geeft de som van zijn twee argumenten terug: "lambda a, b: a+b". Lambda forms kunnen overal gebruikt worden waar je een functie-object nodig hebt. Syntactisch zijn ze beperkt tot een enkele expressie. Semantisch zijn ze gewoon “syntactic sugar” (een syntactisch aardigheidje) voor een gewone functiedefinitie. Net als geneste functiedefinities, kunnen lambda forms verwijzen naar variabelen uit de containing scope:

>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43


4.7.6 Documentatiestrings

Gaandeweg komt de Pythonwereld tot een conventie ten aanzien van de inhoud en opmaak van documentatiestrings.

De eerste regel moet altijd een korte, kernachtige samenvatting zijn van het doel van het object. Om het kort te houden hoeven de naam en het type van het object er niet in voor te komen, aangezien beide langs andere wegen beschikbaar zijn (tenzij de naam toevallig een werkwoord is waarmee de werking van de functie beschreven kan worden). Deze regel moet beginnen met een hoofdletter en eindigen met een punt.

Als de documentatiestring meerdere regels heeft, moet de tweede regel leeg zijn, zodat de samenvatting visueel gescheiden wordt van de rest van de beschrijving. In de daaropvolgende regels worden in één of meer paragrafen zaken beschreven zoals conventies ten aanzien van het aanroepen van het object, bijwerkingen, etc.

De Python-parser wijzigt in string literals die uit meerdere regels bestaan, niets aan de inspringing, dus tools die deze documentatie verwerken en gebruiken moeten indien nodig zelf de inspringing corrigeren. Dit wordt gedaan aan de hand van de volgende conventie. De eerste regel na het begin van de string
(dus na de allereerste regel van de string) die niet leeg is, bepaalt de mate van inspringing voor de hele documentatiestring. (We kunnen niet de allereerste regel gebruiken voor dit doel, aangezien deze meestal direct volgt op de openings-aanhalingstekens van de string; de mate van inspringing is hier dus niet altijd duidelijk.) Alle spaties en tabs die overeenkomen met deze inspringing, worden verwijderd uit het begin van alle regels van de string. Het zou niet moeten voorkomen dat er regels zijn die een kleinere mate van inspringing hebben, maar als dit toch voorkomt, moeten alle spaties en tabs aan het begin verwijderd worden. Tabs moeten eerst in spaties omgezet worden (meestal in 8 spaties) voordat gekeken wordt naar overeenkomst van inspringing.

Hier volgt een voorbeeld van een docstring die meerdere regels omspant:

>>> def mijn_functie():
...     """Doe niets, maar documenteer het wel.
... 
...     Serieus, hij doet echt niets.
...     """
...     pass
... 
>>> print mijn_functie.__doc__
Doe niets, maar documenteer het wel.

    Serieus, hij doet echt niets.

Voetnoten

... object).4.1
Eigenlijk zou call by object reference een betere benaming zijn, aangezien degene die de aanroep doet alle wijzigingen ziet die de aangeroepen functie aanbrengt in het object (bijvoorbeeld het invoegen van items in een list), mocht er een mutable object worden doorgegeven.

Previous Page

Up One Level

Next Page

Python Tutorial

Contents

Index

Vorige: 3. Een informele inleiding in Python Omhoog: Python Tutorial Volgende: 5. Datastructuren
Release 2.3.4, documentation updated on May 20, 2004.
See About this document... for information on suggesting changes.