Presedens og evalueringsrekkefølge
Operator-presedens
En av de aller vanligste feilene som gjøres (f. eks. på eksamen) er at man gjør feil antakelse om hvilken rekkefølge operasjoner i et uttrykk utføres i. For aritmetikk-operasjoner gjelder de samme reglene som i matematikken.
print("Presedens:")
print(2+3*4) # gir 14, ikke 20 ( * har høyere presedens enn + )
print(5+4%3) # gir 6, ikke 0 ( % har høyere presedens enn + )
print(2**3*4) # gir 32, ikke 4096 (** har høyere presedens enn * )
print()
print("Assosiativitet:")
print(5-4-3) # gir -2, ikke 4 (- assosierer venstre-til-høyre)
print(4**3**2) # gir 262144, ikke 4096 (** assosierer til høyre-til-venstre)
Vi gir her en oversikt over noen operatorer rangert fra høyeste til laveste presedens. Parenteser vil alltid overstyre presedens og assosiativitet, og er derfor øverst på listen. Alle operatører untatt **
og relasjonene assosierer venstre-til-høyre dersom det er aktuelt.
Operatorer | |
---|---|
() | Parentes. Det som er mellom parentesene evalueres før en operator utenfor parentesen. |
** | Eksponentiering. Assosierer høyre-til-venstre. |
* / // % | Multiplikasjon, divisjon og modulo. |
+ - | Addisjon og subtraksjon. |
< <= > >= == != in not in | Relasjoner. Disse vil ikke assosiere verken til høyre eller venstre; dersom man har flere slike etter hverandre vil de komponeres som en konjunksjon i stedet. For eksempel, -1 < 0 == False gir det samme svaret som -1 < 0 and 0 == False , og altså ikke det samme som (-1 < 0) == False slik man ellers kunne trodd. |
not | Logisk negasjon. |
and | Logisk konjunksjon. |
or | Logisk disjunksjon. |
if else | Betinget verdi. |
Flere eksempler (se om du kan forutsi hva hvert uttrykk evaluerer til før du kjører koden og ser fasiten):
print(True or True and False)
print(not False or True)
print(not (False or True))
print()
print(2 < 3 < 4)
print(not 3 < 2 < 1)
print(not 3 < 2 and 2 < 1)
print()
print("b" in "box")
print("b" in "box" == True) # (Brython/nettleser krasjer, men prøv i python)
print("a" and "b" in "box")
print("a" or "z" in "box")
Evalueringsrekkefølge og utsatt evaluering
def en():
print("en", end=" ")
return 1
def to():
print("to", end=" ")
return 2
def tre():
print("tre", end=" ")
return 3
def fire():
print("fire", end=" ")
return 4
print("Uttrykk evalueres som hovedregel fra venstre mot høyre")
print(en() * to() + tre()) # en to tre 5
print(en() + to() * tre()) # en to tre 7
print(en() + to() ** tre()) # en to tre 9
print(en()**to()**tre()) # en to tre 1
print(en() and to() and tre()) # en to tre 3
print()
print("Ingen regel uten unntak: ")
print(en() < to() < tre()) # to en tre True
print(en() if to() < tre() else fire()) # to tre en 1
Legg merke til at fire()
aldri blir kjørt på den siste linjen over. Dette kalles utsatt evaluering (engelsk: lazy evaluation), og skjer fordi python prøver å unngå å kjøre kode dersom den ikke trenger det.
Når man benytter logiske konjunksjoner (and
) eller disjunksjoner (or
) skjer det også en variant av dette, som vi kaller kortslutningsevaluering (engelsk: short-circuit evaluation):
def ja():
return True
def nei():
return False
def krasj():
return 1/0 # krasjer!
print(nei() and krasj()) # False
print(krasj() and nei()) # Krasjer!
print (ja() and krasj()) # Ville også krasjet om vi kom hit
Det samme for disjunksjoner (or
):
def ja():
return True
def nei():
return False
def krasj():
return 1/0 # krasjer!
print(ja() or krasj()) # True
print(krasj() or ja()) # Krasjer!
print (nei() or krasj()) # Ville også krasjet om vi kom hit
Vi kan utnytte kortslutningsevaluering for å unngå å krasje.
def solid_sjekk(tekst):
""" Sjekker om første bokstav i teksten er 'a' """
return len(tekst) > 0 and tekst[0] == 'a'
def ubeskyttet_sjekk(tekst):
""" Sjekker om første bokstav i teksten er 'a' """
return tekst[0] == 'a'
print(solid_sjekk("ananas")) # True
print(ubeskyttet_sjekk("ananas")) # True
print()
print(solid_sjekk("banan")) # False
print(ubeskyttet_sjekk("banan")) # False
print()
print(solid_sjekk("")) # False
print(ubeskyttet_sjekk("")) # Krasjer (ops!)
I kombinasjon med at at de logiske operasjonene assosierer venstre-til-høyre, vil en rekke med konjunksjoner (and
) returnere den første (lengst til venstre) falsy verdien i rekken; eventuelt den siste verdien dersom det ikke var noen falsy verdier i kjeden:
def ja():
print("Ja-funksjonen blir kalt")
return True
print(3 and "False" and None and -2 and ja() and False) # None
print(3 and ja() and "Hurra!") # Hurra! (kjører også ja-funksjonen)
En rekke med disjunksjoner vil på samme måte returnere den første truthy verdien:
def ja():
print("Ja-funksjonen blir kalt")
return True
print(0 or "" or 10 or False or None or ja()) # 10