2.15. Funktionsdefinering og -kald

Under udviklingen af et program kan der være brug for en hensigtsmæssig måde at opdele programmet på, så vi kan nøjes med at beskæftige os med en del af programmet ad gangen. Vi kan ønske at putte mere trivielle rutiner hen i et hjørne af programmet, så de ikke forstyrrer os i den øvrige programskrivning. Her er funktioner (functions) eller som de samme også ofte kaldes metoder (methods) en velkendt hjælp i de fleste computersprog om ikke alle de nuværende. I det oprindelige BASIC kendtes begrebet ikke, hvorfor vi brugte "GOTO" i massevis. I Pascal og Delphi er der ud over funktionerne nogle til funktioner forholdsvis svarende "pakkesamlere", der kaldes procedurer. Procedurer kan returnere en værdi, men behøver det ikke. Alt efter computersprog kan en funktion ubetinget skulle returnere en værdi (gælder i C++), i andre computersprog som Pascal, Delphi, Visual Basic og Python kan en funktion returnere en værdi, men behøver det ikke nødvendigvis. Python funktioner bliver derfor, hvad C++ programmører nok vil kalde en procedure. Men lad os nu skrive en Python funktion af hver slags - først en funktion, der returnerer en værdi (altså den "rigtige" funktion).

Når du er færdig med defineringen skal du trykker 2 gange på Enter-tasten dels for at fortælle Python, at defineringen er færdig og dels for at komme tilbage til interaktiv mode markeringen


>>>
Funktionsdefinering:
>>> def funktion():-
...     a = 4 * 5
...     return a
...
Funktionskald:
>>> funktion()
20


Som du kan se ovenfor, skal nøgleordet def bruges for at fortælle Python, at det kommende er en funktionsdefinering. Efter nøgleordet følger funktionsnavnet, der kan være alle lovlige navne som i variabler. Efter funktionsnavnet følger en parameterliste, der som her kan være tom. Den omgives af runde parenteser og efterfølges af det kolon, som du med garanti vil glemme mange gange, inden du er inde i rutinen med at fortælle Python, at det kommende er en sammenhørende blok (her funktionskroppen). Python reagerer ved at rykke de efterfølgende programlinjer en tabulatorbredde til højre. Hvis du kopierer kode fra eksempelvis en editor til Python, er det nødvendigt, at få nævnte indrykninger med evt. ved selv at indsætte dem ved at trykke på tastaturets tabulatortast. Der er ofte flere blokke i en funktion. Er der det, skal de programlinjer, der hører til den pågældende blok rykkes endnu et tabulatorstop til højre.

Returværdien kan også indgå i eksempelvis en anden regneoperation:


>>> print funktion() * 7
140


Funktion med et parameter (kaldes undertiden et argument):

>>> def f(a): ... return a * 1.25 ... >>> f(75) 93.75

Funktion med mere end et parameter:

>>> def funktionsnavn(s1,s2): ... return s1 + s2

Funktionskald:

>>> funktionsnavn("Streng1 ", "streng2.") 'Streng1 streng2.

Samme funktion, men ved tildeling af numeriske værdier:

>>> funktionsnavn(10,20.50) 30.5

Brugerdefineret funktion kalder anden brugerdefineret funktion:

>>> def f1(): ... print f2() ... >>> def f2(): ... print "Funktion f2 er blevet kaldt." ... >>> f1() Funktion f2 er blevet kaldt. None

Brugerdefineret funktion kalder fordefineret funktion:

>>> from sys import exit >>> def afslut(): ... sys.exit() ... >>> afslut() ajbo@linux:~>

>>> m = input("Skriv denne maaneds nummer: ") Skriv nummeret (pladsen i kalenderen) for denne maaned: 12 >>> maanedsnavne = ["januar","februar","marts","april","maj","juni","juli",\ ... "august","september","oktober","november","december"] >>> if 1 <= m <= 12: ... print "Denne maaneds navn er",maanedsnavne[m - 1]

Denne maaneds navn er december

Liste kan modtages som argument i funktion: >>> liste [] #opretter tom global liste >>> def ul(l): # argumentet l erklæres her som lokalt navn ... liste.append(l) # udvider den globale liste ... >>> ul(1) >>> ul("Hasle") >>> ul(2) >>> ul("Nyker") Kørselsresultat: >>> liste [1, 'Hasle', 2, 'Nyker']

>>> liste = [1,2,3,4] >>> def udvL(l = []): # argumentet l erklæres her som lokalt navn ... for i in range(5,11): ... l.append(i) ... print l ... Kørselsresultat: >>> udvL(liste) [1, 2, 3, 4, 5] [1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6, 7] [1, 2, 3, 4, 5, 6, 7, 8] [1, 2, 3, 4, 5, 6, 7, 8, 9] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] >>>

def kR( lengde = 1, bredde = 1, hojde= 1 ): return lengde * bredde * hojde

print "Forvalgte kasserumfang:", kR() print "Rumfang af kasse med længden 10 er: ", kR( 10 ) print "Rumfang af kasse med højden 10 er: ", kR(1 ,10 ) print "Rumfang af kasse med længde og højde 10 er: ", kR(10 ,10 ) print "Rumfang af kasse med længde 12, bredde 4 og højde 10 er: ", kR(12,4 ,10 )

Find primtal: >>> for i in range(2, 10): ... for j in range(2, i): ... if i % j == 0: ... print i, 'er lig med', j, '*', i/j ... break ... else: ... print i, 'er et primtal' ... 3 er et primtal 4 er lig med 2 * 2 5 er et primtal 5 er et primtal 5 er et primtal 6 er lig med 2 * 3 7 er et primtal 7 er et primtal 7 er et primtal 7 er et primtal 7 er et primtal 8 er lig med 2 * 4 9 er et primtal 9 er lig med 3 * 3 >>>

Beregn fakultet ved rekursion: def fakultet( tal ): if tal <= 1: return 1 else: return tal * fakultet( tal - 1 ) # rekursivt kald

for i in range( 1,11 ): print "%2d! = %d" % ( i, fakultet( i ) )

ajbo@linux:~> python fakultet.py 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800


>>> def F():
>>>     F()
...
>>> F.ekstra
23
>>>



def OK(prompt, rundgang=4, svar='Svar ja eller nej!'):
    while True:
        ok = raw_input(prompt)
        if ok in ('JA', 'Ja', 'ja'): return 1
        if ok in ('NEJ', 'Nej', 'nej'): return 0
        rundgang = rundgang - 1
        if rundgang < 0: raise IOError, 'vanskelig bruger'
        print svar
Kan kaldes med eksempelvis:
OK('Ønsker du at afslutte?')
og:
OK('Ønskes filen lukket?', 2)



>>> i = 5
>>> def f(arg = i):
...     print i
...
>>> f()
5
>>>



>>> def f(a, L=[]):
    L.append(a)
    return L... ...
...
Et kørselsresultat:
>>> for i in range(10):
...     print f(i)
...
[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>>



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



>>> for i in range(10):
...     print f(i),
...
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
>>>



Returner variablen None:
>>> def f():
...     return
...



>>> print f(56)
None
>>>


Nøgleords argumenter (keyword arguments). Du kan vælge at lade din funktion have et antal fordefinerede og forud værditildelte nøgleord. Sådanne nøgleord må ikke forveksles med de i Python forud definerede variabler. Vi skal se et eksempel:


>>> def funktion(computer,kvalitet, styresystem = "Linux", computersprog = "Python"):
...      print "En",kvalitet,computer, "anvender", styresystem
...
>>> funktion("PC","veludstyret")
En veludstyret PC anvender Linux
>>>
Argumenterne computer og kvalitet er tomme, hvorfor de skal tildeles værdi ved funktionskald. Det skal nøgleordsargumenterne ikke, da de allerede er tildelt en sådan. Det samme gælder nøgleordet computersprog.

Som andre variabler, kan variablen kvalitet modtage en tom værdi, ellers kan du få et lidt ulogisk resultat som:


>>> funktion(0,0)
En 0 0 anvender naturligvis Linux
>>>
>>> funktion("PC","")
En  PC anvender Linux
>>>
>>> funktion("","veludstyret")
En veludstyret  anvender Linux
>>>
>>> funktion("PC","nogenlunde",styresystem = "Windows")
En nogenlunde PC anvender Windows
>>>


Når en funktions sidste parameter er af formen **navn (med 2 foranstillede stjerner), kan den modtage en ordliste, hvis nøgleord ikke findes i parameterlisten. Det kan kombineres med en formel parameter af formen *navn (med 1 foranstillet stjerne).*navn skal komme før **navn:


>>> def f(vareart, *argumenter, **noegleord):
...     pass
...
>>>
Funktionskald af den type bruges meget i Python, så det er vigtigt at forstå virkning m.v, hvorfor jeg viste første eksempel med danske navne, ellers er det mest praktist at holde sig til de engelske, da det er dem, du normalt vil se:


>>> def f(programnavn, *arguments, **keywords):
...     return programnavn, arguments, keywords
...
>>> f("mitProgram.py")
('mitProgram.py', (), {})
>>>


Som du ser, returneres der en tuple indeholdende 1: argumentet 2: en tuple og 3: en ordliste. Hvis du ønsker at læse den eksterne fil mitProgram.py, kan du anvende følgende indledning:


>>> f("mitProgram.py","r")
('mitProgram.py', ('r',), {})
>>>


>>> f("mitProgram.py","r","r2","r3") ('mitProgram.py', ('r', 'r2', 'r3'), {}) >>>

>>> f("mitProgram.py","r","r2","r3", regnskabsaar = 2003) ('mitProgram.py', ('r', 'r2', 'r3'), {'regnskabsaar': 2003}) >>>

>>> f("mitProgram.py","r","r2","r3",regnskabsmaaned = "januar") ('mitProgram.py', ('r', 'r2', 'r3'), {'maaned': 'januar'}) >>> >>> f("mitProgram.py","r","r2","r3",maaned = "februar") ('mitProgram.py', ('r', 'r2', 'r3'), {'maaned': 'februar'}) >>> >>> f("mitProgram.py","r","r2","r3",maaned = "januar",md2 = "februar") ('mitProgram.py', ('r', 'r2', 'r3'), {'md2': 'februar', 'md': 'januar'}) >>>

Argumentlisten behøver ikke nødvendigvis at være af et omfang som en dansk stil:

>>> def f(v,*a, **k): ... return v,a,k ... >>> f("Vareart","cykel",navn = "Christiania") ('Vareart', ('cykel',), {'navn': 'Christiania'}) >>> >>> f("Vareart","cykel",navn2 = "Christiania") ('Vareart', ('cykel',), {'navn2': 'Christiania'}) >>>

>>> def f(v,*a, **k): ... print a, k ... >>> f("Vareart","cykel",navn = "Christiania") ('cykel',) {'navn': 'Christiania'}

>>> def f(a,*b,**c): ... c = {1:"en",2:"to",3:"tre"} ... return a,b,c ... >>> f(1,2) (1, (2,), {1: 'en', 2: 'to', 3: 'tre'}) >>>

Bemærk: boglisten kræver de krøllede parenteser: >>> def f(a,*b,**c): ... return a,b,c ... ...

>>> f(1,"fire","fem","seks",7:"syv",8:"otte",9:"ni") File "<stdin>", line 1 f(1,"fire","fem","seks",7:"syv",8:"otte",9:"ni") ^ SyntaxError: invalid syntax >>> f(1,"fire","fem","seks",{7:"syv",8:"otte",9:"ni"}) (1, ('fire', 'fem', 'seks', {8: 'otte', 9: 'ni', 7: 'syv'}), {}) >>>

>>> def f(a,*b,**c): ... for arg in b : print arg ... >>> f(1,"to","tre","fire") to tre fire >>>

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

>>> args = [3, 6] >>> range(*args) [3, 4, 5]


Lamda arbejder i baggrunden:
>>> def svindel(n):
...     return lambda x: x + n
...
>>> f = svindel(1.25)
>>> f(100)
101.25
>>>