Start | variabelt antal argument
 

Funktioner med variabelt antal argument



*lista

Vi kan skapa en funktion i vilken det går att skicka in variabelt antal argument. Med en liten stjärna * framför parametervariabeln så kommer funktionen tolka argumenten som en lista. Det visar sig att detta är ruskigt användbart.

Så, vi kan sedan loopa över listan precis som vi gör med listor. Titta här nedan. Vi skickar in 10, 20, 30 och detta tolkas som en lista [10, 20, 30]. Poängen här, det är ju att vi kan anropa med valfritt antal argument. Funktionen betraktar det sedan som en lista, tack vare *stjärnan.
def summa(*lista): sum = 0 for i in range(0,len(lista)): sum = sum + lista[i] return(sum) s = summa(10,20,30) print(s)
Men vi kan också summera listan snyggare genom att skippa "in range", dvs strunta i index och istället fånga elementet i varje varv, vilket du lärt dig under loopar. Vilken metod som är bäst att använda beror helt på om man har någon användning av indexet eller inte. Ofta har man inte det.
def summa(*lista): sum = 0 for tal in lista: sum = sum + tal return(sum) s = summa(1,2,3,4,5,6,7,8,9) print(s)
På motsvarande sätt kan vi skapa en funktion maxvarde eller minvarde, som berättar vad största eller minsta värdet är.
def maxvarde(*lista): max = -9999999999 for tal in lista: if tal > max: max = tal return(max) def minvarde(*lista): min = 9999999999 for tal in lista: if tal < min: min = tal return(min) print(maxvarde(1,2,3,4,5,6,7,8,9)) print(minvarde(1,2,3,4,5,6,7,8,9))
Det är ju såhär t.ex. print -funktionen fungerar, eller hur?
def min_print(*argument): for s in argument: print(s,end=" ") min_print("Hej","På","Dig","Din","Rackare","Där")

funktion som argument


Vi kan skicka en funktion som argument till en funktion och som kör denna funktion på en variabelt lång lista av tal.
def min_map(funktion, *lista): returlista = [] for tal in lista: returlista.append(funktion(tal)) return returlista def dubbla(tal): return(tal*2) q = min_map(dubbla, 3,4,5,6,7,) print(q)
Det hade förstås fungerat med lambda -funktion här ...
def min_map(funktion, *lista): returlista = [] for tal in lista: returlista.append(funktion(tal)) return returlista q = min_map(lambda x:x*2, 3,4,5,6,7,) print(q)

mer exempel


I fysiken lärde vi oss räkna ut parallell -kopplade resistanser.
def parallell(*resistorer): res = 0 for r in resistorer: res = res + 1/r return(1/res) r1 = parallell(10,10) r2 = parallell(10,10,10,10) print("r1=",r1,"Ω") print("r2=",r2,"Ω")
Så, r1 borde bli 5 Ω och r2 borde bli 2.5 Ω

serie/parallell


Vi kör ett till exempel från fysiken, för att illustrera funktioner som argument till funktioner plus variabelt antal argument.

Alla resistanser nedan 1 Ω
Så vi har, om vi vandrar medurs runt i kretsen, 1 Ω i serie med 2 parallella 1 Ω i serie med 2 parallella 1 Ω i serie med en parallell grej där ena halvan är 1 Ω och den andra 3 seriekopplade 1 Ω, slutligen i serie med 1 Ω. Glasklart? Formlerna är följande.

Seriekoppling, vi summerar alla R. Ganska enkelt.
Parallellkoppling, vi summerar inversen av resp. R och inverterar sedan denna summa.
def serie(*resistorer): res = 0 for r in resistorer: res = res + r return(res) def parallell(*resistorer): res = 0 for r in resistorer: res = res + 1/r return(1/res) r = serie(1,parallell(1,1), parallell(1,1), parallell(1,serie(1,1,1)), 1) print("r=",r,"Ω")
Stämmer det? Titta i en simulator för elkretsar. Ska bli 3.75 Ω. Du kan räkna på kretsscheman med kondensatorer och spolar också. Se exempel under komplexa tal.

Tolka färgkoder


Mer exempel från fysiken. Var är resistansen mellan A och B.



Vi kan utveckla koden lite till, så att vi slipper tolka alla dessa färgkoder.

  1:a 2:a 3:e (multiplikator) Tol.
 Svart 0 0 x 1 Ω
 Brun 1 1 x 10 Ω 1 %
 Röd 2 2 x 100 Ω 2 %
 Orange 3 3 x 1.000 Ω
 Gul 4 4 x 10.000 Ω
 Grön 5 5 x 100.000 Ω
 Blå 6 6 x 1.000.000 Ω
 Violett 7 7 x 10.000.000 Ω
 Grå 8 8
 Vit 9 9
 Silver x 0,01 Ω 10 %
 Guld x 0,1 Ω 5 %


import math def serie(*resistanser): res = 0 for r in resistanser: res = res + r return(res) def parallell(*resistanser): res = 0 for r in resistanser: res = res + 1/r return(1/res) def farg(*farger): res = 0 i = 0 v = 0 ant_band=len(farger) for farg in farger: i = i + 1 match farg: case "brun": v = 1 case "röd": v = 2 case "orange": v = 3 case "gul": v = 4 case "grön": v = 5 case "blå": v = 6 case "lila"|"violett": v = 7 case "grå": v = 8 case "silver": v = -2 case "guld": v = -1 if(ant_band == 3): if(i==1): res = res + v*10 if(i==2): res = res + v*1 if(i==3): res = res*10**v if(ant_band == 4): if(i==1): res = res + v*100 if(i==2): res = res + v*10 if(i==3): res = res + v if(i==4): res = res*10**v return(res) R = serie( farg("röd","röd","brun"), parallell( farg("orange","orange","brun"), serie( farg("brun","svart","brun"), farg("brun","svart","brun"), farg("brun","grön","brun"))), farg("röd","röd","brun")) print(round(R),'Ω')

Titta i elkrets simulatorn om det stämmer

**dict

Med två ** framför vår parametervariabel, så omtolkas argumentet till en dictionary.
def visa_argumenten(**dict): print(dict) visa_argumenten(forsta='kalle', andra='pelle', miljonte='gurka')
Som du ser om du kör detta, så på insidan av funktionen blev argumenten en dictionary!
def visa_argumenten(**dict): for key, value in dict.items(): print("%s = %s" % (key, value)) visa_argumenten(forsta='kalle', andra='pelle', miljonte='gurka')
Eller som nedan. Vi kan komma åt argumentens värde precis som den dictionary det blivit.
def visa_argumenten(**dict): print(dict["forsta"]) print(dict["andra"]) print(dict["miljonte"]) visa_argumenten(forsta='kalle', andra='pelle', miljonte='gurka')
Observera att vi förstås hade kunna skicka in en verklig dictionary. Dvs vi tar bort våra dubbla ** och formulerar argumentet som en verklig dictionary. Men det blir betydligt pratigare. Det finns fördelar med att skicka in en verklig dictionary ibland.
def visa_argumenten(dict): print(dict["forsta"]) print(dict["andra"]) print(dict["miljonte"]) visa_argumenten({'forsta':'kalle', 'andra':'pelle', 'miljonte':'gurka'})
När vi ändå labbar lite med ** och att skicka in en verklig dictionary. Vi kan skicka in en dictionary med två ** framför sig och den kommer då omtolkas till namnen på funktionens parametrar.
def volym(hojd,bredd,djup): print("hojd",hojd) print("bredd",bredd) print("djup",djup) volym(**{'hojd':5, 'djup':20, 'bredd':10})
Eller så kanske det ser ut såhär ...
def volym(hojd,bredd,djup): print("hojd",hojd) print("bredd",bredd) print("djup",djup) kub_dim = {'hojd':5, 'djup':20, 'bredd':10} volym(**kub_dim)


Som du kommer ihåg från avsnittet med dictionary, så är poängen med att använda dict.get("t") istället för dict["t"] att med get() så får vi svaret None om variabeln saknas. Utan denna konstruktion, så hade vi fått Error om en variabel saknas. Nu kan vi smidigt kolla vilka värden som finns!
def svt(**dict): if(dict.get("t")!=None and dict.get("v")!=None): return(dict["t"]*dict["v"]) elif(dict.get("s")!=None and dict.get("v")!=None): return(dict["s"]/dict["v"]) elif(dict.get("s")!=None and dict.get("t")!=None): return(dict["s"]/dict["t"]) else: return 0 print("fart=", svt(s=100, t=2))

*lista och **dict

Vi kan kombinera dem och python kommer mönstermatcha för att se vilken del av argumenen som är *lista och vilken del som är **dict.
Det går även att använda *lista och **dict i t.ex. __init__ -funktionen i klasser.

Lite frågor


def summa(*lista):
    sum = 0    
    for tal in lista:
        sum = sum + tal
    return(sum)
Ovan funktion returnerar summan av tal. Hur ser ett anrop till funktionen ut?
  summa({1,2,3})
  summa([1,2,3])
  summa(1,2,3)


24.256944656372 ms