Kapittel 3 Betingede setninger


Gratulerer med at du fikk en bedre forståelse av datatyper i forrige kapittel. Du skal i dette kapittelet lære å gjøre programmene dine mer fleksible med betingede setninger. Dette er et sentralt konsept i programmering, og de fleste nyttige programmer bruker betingede setninger. Arbeidet vårt med betingede setninger vil tvinge oss til å jobbe mer med sannhetsverdiene True og False som vi ble introdusert til i seksjon 2.5. I tillegg skal du lære om anbefalinger knyttet til god kodestil i Python i seksjon 3.6.

3.1 Introduksjon - Programmene våre er ikke dynamiske!

Så langt har programmene våre en stor begrensning. De gjør alltid de samme valgene! Tenk på et videospill som en analogi. Et videospill endrer seg basert på forskjellige biter informasjon. Denne informasjonen kan være tastetrykk fra brukeren, hvor mange liv en spiller har, eller hvor mange runder du har igjen i et Formel 1-løp. Det vi så langt har laget er litt som et videospill der det samme alltid skjer.

I dette kapittelet skal vi lære hvordan vi kan bruke betingede setninger31 til å ta forskjellige valg. La oss gi to eksempler på hvor surt livet er uten betingede setninger:

  • Sett at du skal skrive et program som skal håndtere innlogging for brukere. Brukeren skal skrive inn brukernavn og passord. Vi har lært i kapittel 2 at vi kan bruke funksjonen input() til å hente inn brukernavnet og passordet. Men siden vi ikke ennå kan bruke betingede setninger, så må vi enten alltid godkjenne eller alltid avslå innloggingen. Samme hva brukeren skriver inn! Dette ødelegger hele programmet vårt.

  • Sett at du heller prøver deg på å utvikle et program som skal sjekke om stiler som studenter har levert inn er over 1500 ord. Igjen uten betingede setninger så blir du fint nødt til å enten godkjenne alle innleveringer, eller å avslå alle innleveringer. Dette programmet kommer nok heller ingen til å bli særlig imponert over.

Frykt ikke! Vi skal i dette kapittelet lære hvordan vi skriver betingede setninger i Python. I prosjektdelen i seksjon 3.7 skal vi bruke det vi har lært til å beregne priser på bussbilletter. Programmet vi lager skal ta hensyn til om passasjeren er et barn, en voksen, en pensjonist, eller en student.

3.2 Enkle if-else-setninger

La oss hoppe rett i det. Det enkleste eksempelet på en betinget setning i Python er en if-setning32. Dette ser slik ut:

bussbillett_pris = 55

if bussbillett_pris > 50:
    print('Det var dyrt, ass!')

Du kan lese koden over i hodet ditt som følger: Dersom prisen på bussbilletten er større enn 50 kroner, skriv ut ‘Det var dyrt, ass!’. La oss gå litt rolig gjennom hva som skjer etter at variabelen bussbillett_pris har blitt definert:

  • Først bruker vi nøkkelordet if sammen med en betingelse. I vårt tilfelle er betingelsen bussbillett_pris > 50. Vi spør altså om bussbillett_pris er større enn verdien 50. Vi slutter linjen med et kolonsymbol :. Dette symbolet indikerer at koden under, som er innrykket til høyre med 4 mellomrom, tilhører denne if-setningen. Vi sier at koden etter symbolet : som er innrykket til høyre er en kodeblokk33 som hører til if-setningen.

  • Koden print('Det var dyrt, ass!') tilhører altså if-setningen. Den vil bare bli utført dersom betingelsen bussbillett_pris > 50 blir evaluert til å være sann. I vårt tilfelle er den sann siden bussbillett_pris har verdien 55. Prøv å forandre verdien til variabelen bussbillett_pris til å være lik 45. Kjører du nå koden så vil ingenting bli skrevet ut.

Merk at koden over gjør et valg. Dersom bussbillett_pris holder en verdi større enn 50 så blir noe skrevet ut til terminalen. Dersom verdien er mindre eller lik 50, så blir ingenting skrevet ut. Dette er et veldig enkelt valg, men mer kompliserte konsepter bygger på det samme prinsippet.

Vi nevnte at kodeblokken i en if-setning er innrykket til høyre med 4 mellomrom. Her er egentlig ikke tallet 4 noe magisk, og du kan også bruke for eksempel 2 mellomrom om ønskelig. Vær oppmerksom på at mange har sterke meninger om dette stilistiske valget. Det er en konvensjon i Python at man bruker 4 mellomrom, og du er på trygg grunn dersom du følger dette. Du lærer mer om konvensjoner i Python i seksjon 3.6.

Dersom du glemmer å flytte kodeblokken til høyre overhodet slik som i koden under, så vil du få en IndentationError. Prøv å kjøre koden under for å sjekke dette:

bussbillett_pris = 55

if bussbillett_pris > 50:
print('Det var dyrt, ass!')

Det er veldig enkelt å fikse en IndentationError siden den som regel bare kommer av litt slurv. Legg på 4 mellomrom før funksjonen print() og kjør koden igjen for å se at dette fikser problemet.

3.2.1 Sammenligningsoperatorer

Hvilke uttrykk kan vi bruke som betingelser i if-setninger? I Python er det seks sammenligningsoperatorer34 som kan brukes til å danne betingelser:

  • Mindre enn: Dette blir brukt med symbolet < slik som a < b. Betingelsen er da sann om a er strengt mindre enn b.

  • Mindre enn eller lik: Dette blir brukt med symbolet <= slik som a <= b. Betingelsen er da sann om a er mindre eller lik b.

  • Større enn: Dette blir brukt med symbolet > slik som a > b. Betingelsen er da sann om a er strengt større enn b.

  • Større enn eller lik: Dette blir brukt med symbolet >= slik som a >= b. Betingelsen er da sann om a er større eller lik b.

  • Lik: Dette blir brukt med symbolet == slik som a == b. Betingelsen er da sann om a er lik b.

  • Ulik: Dette blir brukt med symbolet != slik som a != b. Betingelsen er da sann om a er ulik b.

Det er en vanlig begynnerfeil å bruke = som sammenligningsoperator for likhet fremfor ==. Dette er ikke så rart, siden symbolet = brukes som likhet i matematiske ligninger. I Python derimot er skillet skarpt. Symbolet = brukes for å sette en variabel til en verdi, mens == brukes til å sammenligne to verdier for likhet i en betinget setning.

Her er et eksempel som tar inn navnet til brukeren og sjekker om dette er favorittnavnet:

favorittnavn = 'Madelene'

navn = input('Hva er navnet ditt? ')

if navn == favorittnavn:
    print('Så fint navn! Det er min favoritt!')

Hvis du prøver å kjøre koden ovenfor og skriver noe annet inn enn Madelene så vil ingenting skje. Den fine beskjeden får bare de som heter Madelene. Hva med alle andre? Burde ikke de få en beskjed de også, om kanskje ikke like hyggelig?

Vi kan legge på en else-setning35 med nøkkelordet else for å gjøre en handling for de som ikke får den første beskjeden:

favorittnavn = 'Madelene'
navn = input('Hva er navnet ditt? ')

if navn == favorittnavn:
    print('Så fint navn! Det er min favoritt!')
else:
    print('Takk for at du skrev inn ditt navn.')

Kjør koden over et par ganger med forskjellige navn. Du skjønner sikkert snart at programmet skriver ut beskjeden Så fint navn! Det er min favoritt! bare når du skriver at navnet ditt er Madelene. Hvis du skriver noe annet, så utløses else-blokken og du får den litt mer tørre beskjeden Takk for at du skrev inn ditt navn..

Vi kaller koden ovenfor for en if-else-setning36 siden den både har en if-blokk og en else-blokk. Dersom betingelsen i if-setningen ikke er opprettholdt, så blir koden i else-blokken utført. Tenk på det som står i else-blokken som noe å falle tilbake på dersom if-setningen ikke er gyldig.

Merk også at dersom du skriver inn madelene med liten forbokstav, så vil du også få den kjipe beskjeden. Dette er fordi Python er sensitiv når det kommer til store og små bokstaver. Strengene Madelene og madelene er ikke like, så koden i if-setningen vil ikke bli utført. En måte å komme seg rundt dette på er å bruke metoden .capitalize() som vi så i seksjon 2.4 slik at betingelsen i if-setningen blir navn.capitalize() == favorittnavn.

Før vi går videre, la oss nevne at sannhetsverdier står sentralt når det kommer til betingede setninger. Se på koden under:

favorittall = int(input('Hva er ditt favorittall? '))
print(favorittall == 7)

Dersom du prøver å kjøre koden ovenfor flere ganger, så vil du få skrevet ut enten True eller False basert på om favorittall er lik 7 eller ikke. Her kan vi se at sammenligningsoperatorer bare gir ut verdiene True eller False når de blir evaluert. Så sannhetsverdiene True og False spiller en viktig rolle for betingede setninger.

3.3 Logiske operatorer

Så langt kan våre if-setninger evaluere enkle betingelser. Iblant er betingelser derimot mer komplekse som følgende eksempel: Sett at du bistår med kode til en nettbutikk. Der skal en bruker betale med kort dersom begge betingelsene under er opprettholdt:

  • Betalingsbeløpet er større enn null.

  • Brukeren har valgt kort som betalingsmåte.

Vi kan representere et scenario for en bruker slik:

beløp = 500
betalingsmåte = 'kort'

Hvordan skal vi nå skrive if-setningen? Vi kan jo prøve å skrive koden slik:

beløp = 500
betalingsmåte = 'kort'

if beløp > 0:
    print('Her kan vi skrive kode for kortbetaling')
  
if betalingsmåte == 'kort':
    print('Her kan vi skrive kode for kortbetaling')

Dette fungerer dessverre ikke. Det skal være nødvendig at begge betingelsene må være opprettholdt, ikke bare en av dem. I tillegg må brukeren nå gå gjennom kortbetalingen to ganger hvis begge betingelsene er opprettholdt. En måte å løse dette på er å legge den ene if-setningen inni den andre slik:

beløp = 500
betalingsmåte = 'kort'

if beløp > 0:
    if betalingsmåte == 'kort':
        print('Her kan vi skrive kode for kortbetaling')

Dette fungerer! Nå må begge betingelsene være opprettholdt. Dersom betingelsen beløp > 0 ikke er sann, kommer vi ikke inn i kodeblokken til den ytterste if-setningen. Og dersom betingelsen betalingsmåte == 'kort' feiler, kommer vi ikke inn i kodeblokken til den innerste if-setningen.

Ulempen med å gjøre det slik som over er at vi har 2 horisontale nivåer med kode. I blant er flere nivåer med kode nødvendig, men det øker kompleksiteten og gjør kode vanskeligere å lese. Vi skal snakke mer om hvordan unngå flere horisontale nivåer i seksjon 3.5.

En bedre måte å løse problemet vårt på er å bruke nøkkelordet and for å sjekke begge betingelsene samtidig:

beløp = 500
betalingsmåte = 'kort'

if beløp > 0 and betalingsmåte == 'kort':
    print('Her kan vi skrive kode for kortbetaling')

Nøkkelordet and kan bli brukt mellom to betingelser til å sjekke at begge betingelsene er sanne. Dersom en av betingelsene evalueres til å være False, så vil ikke kodeblokken til if-setningen kjøre. Prøv å sett beløp til å være 0 for å sjekke dette i koden over.

I tillegg til nøkkelordet and så er det to andre nøkkelord i Python som hjelper deg med betingelser. Dette er nøkkelordene or og not. De tre nøkkelordene and, or, og not kalles ofte logiske operatorer37 og kan forklares slik:

  • Nøkkelordet and brukes mellom to betingelser for å sjekke at begge er sanne. Så dersom du har betingelse A og betingelse B, så gir A and B ut verdien True bare når både A og B er True. Ellers gir den ut False.
Uttrykk A Uttrykk B Resultat (A and B)
True True True
True False False
False True False
False False False
  • Nøkkelordet or brukes mellom to betingelser for å sjekke at minst en av dem er sann. Så dersom du har betingelse A og betingelse B, så gir A or B ut verdien True om en eller begge av A og B er True. Ellers gir den ut False.
Uttrykk A Uttrykk B Resultat (A or B)
True True True
True False True
False True True
False False False
  • Nøkkelordet not endrer sannhetsverdien til en betingelse. Dersom betingelsen A har sannhetsverdi True, så har not A sannhetsverdien False. Dersom betingelsen A har sannhetsverdi False, så har not A sannhetsverdien True.
Uttrykk A Resultat (not A)
True False
False True

Les gjennom forklaringene og tabellene til de tre logiske operatorene nøye. La oss nå gjøre noen eksempler med nøkkelordene or og not slik at du ser når de er nyttige å bruke.

Sett at en kino tilbyr halv pris for barn under 18 år og voksne over eller lik 65 år. Prisen på en vanlig billett er 120kr. Hvordan kan vi løse dette med å bruke nøkkelordet or? Vi kan skrive følgende kode for å sjekke om en av betingelsene er sann:

pris_billett = 120
alder = 73

if alder < 18 or alder >= 65:
    pris_billett = pris_billett * 0.5
  
print(f'Billetten din koster {pris_billett} kroner.')

I koden over kan du se at vi bruker or til å sjekke om personen er enten under 18 år gammel, eller over eller lik 65 år gammel. Her må vi bruke nøkkelordet or og ikke and siden det holder at en av betingelsene er opprettholdt. I dette eksempelet kan jo ikke begge betingelsene være opprettholdt siden ingen er både under 18 år og over eller lik 65 år gammel. I if-setningens kodeblokk oppdaterer vi verdien til pris_billett til å halveres. Prøv å kjør programmet over med forskjellige verdier for alder for å forstå hvordan det fungerer.

La oss nå se litt på nøkkelordet not. De følgende to if-setningene gir det samme resultatet, og er to sider av samme sak:

alder = 15

if alder < 18:
    print('Du er ikke voksen ennå!')
  
if not alder >= 18:
    print('Du er ikke voksen ennå!')

Dette er fordi setningen Dersom du er under 18 år… er logisk det samme som å si Dersom du ikke er over eller lik 18 år…

I eksempler som ovenfor er nøkkelordet not ikke så veldig nyttig. De fleste vil foretrekke den første if-setningen i koden over fremfor den andre. Men husk at vi har lært tidligere i kapittel 2 at noen metoder returnerer True eller False. Dersom du ønsker å bruke denne returverdien i en if-setning, så kan nøkkelordet not være svært nyttig:

telefonnummer = input('Hva er ditt telefonnummer (uten mellomrom): ')

if not telefonnummer.isdigit():
    print('Du må skrive inn sifre uten mellomrom! Skjerp deg!')

Prøv å kjøre koden over et par ganger der du skriver inn både gyldige og ugyldige telefonnumre. Som du vil merke så vil vi her få en beskjed dersom telefonnummeret vi skriver inn ikke bare er siffer uten mellomrom. Metoden .isdigit() returnerer True dersom strengen den blir brukt på bare har siffer i seg. Siden vi ønsker å gi en streng beskjed hvis dette ikke stemmer, så må vi bruke nøkkelordet not her.

Det kan også være nyttig å kombinere de forskjellige logiske operatorene. I eksempelet over med telefonnummer så hadde det også vært nyttig å sjekke om telefonnummeret har 8 siffer. Det kan vi legge til sammen med nøkkelordet or som følger:

telefonnummer = input('Hva er ditt telefonnummer (uten mellomrom): ')

if not telefonnummer.isdigit() or len(telefonnummer) != 8:
    print('Du må skrive inn 8 sifre uten mellomrom! Skjerp deg!')

Koden over vil sørge for at telefonnummeret inneholder nøyaktig 8 siffer. Her ser du styrken som logiske operatorer gir for å beskrive mer kompliserte betingelser.

3.4 Flere valg: If-elif-else

Vi har sett at med en if-else-setning så kan vi gjøre to forskjellige handlinger basert på om en betingelse er opprettholdt. I noen situasjoner har vi derimot mer enn to valg. Et eksempel på dette kan være å regne ut mengden skatt en person må betale. Dette avhenger av mange ting som for eksempel inntekt og bostedssituasjon. I slike situasjoner er ikke if-else-setninger generelle nok. Vi kan derimot bruke en såkalt if-elif-else-setning38 for slike situasjoner.

La oss gå gjennom et annet eksempel i detalj for å forstå if-elif-else-setninger. Se for deg at du er en mattelærer på videregående som har akkurat rettet besvarelsene til en matteprøve. Det går maksimalt an å oppnå 100 poeng på prøven. Du har bestemt poeng, men det gjenstår å sette karakterer fra 1 til 6 på hver besvarelse. Du har bestemt deg for følgende karaktergrenser:

  • Dersom eleven fikk 92 poeng eller mer, så blir karakteren 6.

  • Dersom eleven fikk 80 poeng eller mer, men under 92 poeng, så blir karakteren 5.

  • Dersom eleven fikk 66 poeng eller mer, men under 80 poeng, så blir karakteren 4.

  • Dersom eleven fikk 52 poeng eller mer, men under 66 poeng, så blir karakteren 3.

  • Dersom eleven fikk 40 poeng eller mer, men under 52 poeng, så blir karakteren 2.

  • Dersom eleven fikk under 40 poeng, så blir karakteren 1 (au da!).

Å sette karakteren er kjedelig arbeid når du vet poengsummen. I tillegg er det mulig du gjør en feil siden du er trøtt. Så hvorfor ikke skrive et program i Python som gjør dette? Gitt en poengsum så skal programmet gi ut hvilken karakter eleven bør få. Merk at en vanlig if-else-setning ikke duger her, siden det er seks forskjellige situasjoner som er mulig, ikke bare to. Vi bruker derfor en if-elif-else-setning som ser slik ut:

poengsum = int(input('Hvilken poengsum fikk eleven? '))

if poengsum >= 92:
    print('Eleven skal få karakteren: 6')
elif poengsum >= 80 and poengsum < 92:
    print('Eleven skal få karakteren: 5')
elif poengsum >= 66 and poengsum < 80:
    print('Eleven skal få karakteren: 4')
elif poengsum >= 52 and poengsum < 66:
    print('Eleven skal få karakteren: 3')
elif poengsum >= 40 and poengsum < 52:
    print('Eleven skal få karakteren: 2')
else:
    print('Eleven skal få karakteren: 1')

Les koden over nøye, og kjør gjennom den et par ganger. Her bruker vi først en if-setning som vanlig for å sjekke om poengsum er større eller lik 92. Deretter bruker vi nøkkelordet elif. Dette er etterfulgt av en betingelse og en kodeblokk som blir utført dersom betingelsen er opprettholdt. Som du ser har vi fire elif-setninger som representerer karakterene 5, 4, 3, og 2. Til slutt bruker vi en else-setning siden den eneste gjenstående muligheten er at eleven vil få karakteren 1 og dessverre stryke på matteprøven.

En ting som er verdt å nevne er at betingelsene i en if-elif-else-setning blir evaluert fra øverst til nederst. Så dersom betingelsen poengsum >= 80 and poengsum < 92 blir evaluert til å være sann, så blir den korresponderende kodeblokken utført og resten av if-elif-else-setningen blir hoppet over. Dette gjør at du kan forenkle koden litt. Det er faktisk ikke nødvendig med nøkkelordet and overhodet her, og vi kan forenkle koden til dette:

poengsum = int(input('Hvilken poengsum fikk eleven? '))

if poengsum >= 92:
    print('Eleven skal få karakteren: 6')
elif poengsum >= 80:
    print('Eleven skal få karakteren: 5')
elif poengsum >= 66:
    print('Eleven skal få karakteren: 4')
elif poengsum >= 52:
    print('Eleven skal få karakteren: 3')
elif poengsum >= 40:
    print('Eleven skal få karakteren: 2')
else:
    print('Eleven skal få karakteren: 1')

Hvordan er dette mulig? Se igjen på den tidligere betingelsen poengsum >= 80 and poengsum < 92 i den første elif-setningen som et eksempel. Vi har altså kommet oss forbi if-setningen med betingelsen poengsum >= 92. Det betyr at denne betingelsen må være usann. Så vi vet allerede at poengsum < 92. Derfor kan vi forenkle det kombinerte utsagnet poengsum >= 80 and poengsum < 92 til poengsum >= 80. Å tenke grundig gjennom hvordan en if-elif-else-setning fungerer gjør ofte koden enklere.

Et annet eksempel vi kan gjøre med if-elif-else-setninger er å bygge videre på følgende kode vi så i seksjon 3.3:

favorittnavn = 'Madelene'
navn = input('Hva er navnet ditt? ')

if navn.capitalize() == favorittnavn:
    print('Så fint navn! Det er min favoritt!')
else:
    print('Takk for at du skrev inn ditt navn.')

Når du kjører programmet så kan en bruker trykke enter for raskt, og dermed skrive inn en tom streng. Da vil du med programmet over få skrevet tilbake beskjeden: Takk for at du skrev inn ditt navn. Her hadde det vært greit å få en beskjed om at navnet er tomt. En enkel if-elif-else-setning fikser dette:

favorittnavn = 'Madelene'
navn = input('Hva er navnet ditt? ')

if navn.capitalize() == favorittnavn:
    print('Så fint navn! Det er min favoritt!')
elif navn == '':
    print('Du skrev inn et tomt navn!')
else:
    print('Takk for at du skrev inn ditt navn.')

Her ser vi at elif-setninger kan lett brukes til å utvide eksisterende kode til å dekke flere spesielle situasjoner.

Et triks er at du kan forkorte betingelsen elif navn == '': til elif not navn:. Dette kalles implisitt sannhetskonvertering39. Det som faktisk skjer her er at dersom Python møter på en variabel der den forventer en betingelse, så vil den prøve å konvertere variabelen til en sannhetsverdi. Du kan selv konvertere variabler til en sannhetsverdi ved å bruke funksjonen bool() slik:

print(bool('Hei'))
print(bool(''))

Som du ser hvis du kjører koden over så vil bool('Hei') gi oss True, mens bool('') gir oss False. Det er bare den tomme strengen som blir konvertert til False, mens alle andre strenger blir konvertert til True. På grunn av dette så har alle de tre if-setningene under helt like betingelser:

navn = input('Hva er navnet ditt? ')

if navn == '':
    print('Du skrev inn et tomt navn!')
    
if not bool(navn):
    print('Du skrev inn et tomt navn!')

if not navn:
    print('Du skrev inn et tomt navn!')

Du vil sjelden trenge å bruke funksjonen bool() eksplisitt. Jeg anbefaler heller å bruke implisitt sannhetskonvertering siden det er mer leselig. Av valgene over så synes jeg derfor at koden if not navn: er best.

3.5 Forenkling av kompliserte betingede setninger

Vi har så langt sett på betingede setninger som if-setninger, if-else-setninger, og if-elif-else-setninger. Noen ganger vil du komme borti situasjoner der du skriver betingede setninger som fungerer, men de kan bli skrevet bedre. La oss i denne seksjonen gå grundig gjennom et eksempel for å se hvordan vi kan forbedre kompliserte betingede setninger. Eksempelet vi skal jobbe med er koden:

pris = 500
gyldig_rabatt = True
rabatt_prosent = 0.1

if pris < 250:
    if gyldig_rabatt:
        pris = pris * (1 - rabatt_prosent)
    else:
        print('Ingen rabatt på deg!')
elif pris < 450:
    if gyldig_rabatt:
        pris = pris * (1 - rabatt_prosent * 2)
    else:
        print('Ingen rabatt på deg!')
elif pris < 1_000:
    if gyldig_rabatt:
        pris = pris * (1 - rabatt_prosent * 2.5)
    else:
        print('Ingen rabatt på deg!')
else:
    if gyldig_rabatt:
        if pris >= 5_000:
            pris = pris * (1 - rabatt_prosent * 3.5)
        else:
            pris = pris * (1 - rabatt_prosent * 3)
    else:
        print('Ingen rabatt på deg!')

print(f'Sluttsummen er {pris} kroner.')

Huff! Her var det litt å sette seg inn i. Les sakte gjennom programmet for å forstå hva som vil skje i ulike tilfeller. Prøv også å kjøre programmet med ulike verdier for variabelen pris.

Koden over fungerer helt fint, men den er definitivt mer komplisert enn den trenger. Det ble tre nivåer med innrykk på slutten! Dette gjør at noen linjer er dyttet 12 mellomrom til høyre. Det begynner definitivt å bli litt vanskelig å lese koden.

En annen utfordring er at koden print('Ingen rabatt på deg!') er gjentatt fire steder. Å gjenta kode er et tegn på at koden kan skrives bedre. Ved å fjerne gjentagelse blir koden vår kortere, og dermed lettere å lese. Vi vil i kapittel 6 lære mer om å lage våre egne funksjoner, som er en måte å unngå repetisjon. Her klarer vi oss uten dette ved å omstrukturere logikken

La oss heller sjekke først om det skal bli gitt noen rabatt eller ikke. Ved å trekke denne logikken ut og plassere den først blir koden slik:

pris = 500
gyldig_rabatt = True
rabatt_prosent = 0.1

if not gyldig_rabatt:
    print('Ingen rabatt på deg!')
elif pris < 250:
    pris = pris * (1 - rabatt_prosent)
elif pris < 450:
    pris = pris * (1 - rabatt_prosent * 2)
elif pris < 1_000:
    pris = pris * (1 - rabatt_prosent * 2.5)
else:
    if pris >= 5_000:
        pris = pris * (1 - rabatt_prosent * 3.5)
    else:
        pris = pris * (1 - rabatt_prosent * 3)

print(f'Sluttsummen er {pris} kroner.')

Den nye koden sjekker med en gang om det er noen gyldig rabatt. Hvis ikke så går vi videre til å sjekke betingelsen pris < 250. Betingelsen pris < 250 må nå høre til en elif-setning og ikke en if-setning slik som tidligere. Hvorfor? Betingelsen pris < 250 skal bare evalueres dersom betingelsen not gyldig_rabatt i if-setningen over er usann.

Merk at koden ikke bare har mindre repetisjon, men også bare har to horisontale nivåer med innrykk. Kan vi redusere det til bare ett nivå med innrykk? Hvis du leser over koden i else-setningen over så er det egentlig litt tungvint skrevet. Inni else-setningen sjekker vi først om pris er større enn 5000. Vi kan egentlig bare slette den ytre else-setningen, og gjøre den indre if-setningen til en elif-setning:

pris = 500
gyldig_rabatt = True
rabatt_prosent = 0.1

if not gyldig_rabatt:
    print('Ingen rabatt på deg!')
elif pris < 250:
    pris = pris * (1 - rabatt_prosent)
elif pris < 450:
    pris = pris * (1 - rabatt_prosent * 2)
elif pris < 1_000:
    pris = pris * (1 - rabatt_prosent * 2.5)
elif pris >= 5_000:
    pris = pris * (1 - rabatt_prosent * 3.5)
else:
    pris = pris * (1 - rabatt_prosent * 3)

print(f'Sluttsummen er {pris} kroner.')

Endelig er vi nede i ett nivå. Koden er betydelig enklere å lese nå. Vi kan fortsatt forbedre den.

Rekkefølgen av utsagnene i koden over er fortsatt litt rar. Vi hopper fra betingelsen pris < 1_000, til betingelsen pris >= 5_000, og til slutt vil else-setningen dekke når pris er mellom 1000 og 5000. Hadde det ikke vært mye lettere å bytte om på rekkefølgen slik at vi sjekker gradvis større verdier for pris? La oss gjøre det!

pris = 500
gyldig_rabatt = True
rabatt_prosent = 0.1

if not gyldig_rabatt:
    print('Ingen rabatt på deg!')
elif pris < 250:
    pris = pris * (1 - rabatt_prosent)
elif pris < 450:
    pris = pris * (1 - rabatt_prosent * 2)
elif pris < 1_000:
    pris = pris * (1 - rabatt_prosent * 2.5)
elif pris < 5_000:
    pris = pris * (1 - rabatt_prosent * 3)
else:
    pris = pris * (1 - rabatt_prosent * 3.5)

print(f'Sluttsummen er {pris} kroner.')

Sammenlign hvor mye enklere koden er nå enn den var i starten. Vi bruker ingen nye teknikker for å forenkle koden, men leser bare rolig gjennom den og forbedrer den. Ett steg av gangen.

Vi skal i løpet av boken lære andre måter å skrive om kode på som hjelper med leseligheten. Det er lettere å forsikre seg selv om at leselig kode er riktig. I tillegg er det lettere å utvide leselig kode. Sett at det ble bestemt at når pris ligger mellom 5000 og 10000 så skal det bli gitt en rabatt på 32.5 % fremfor 35 % slik som over. Dette er nå bare å legge på en enkel ny elif-setning slik:

pris = 500
gyldig_rabatt = True
rabatt_prosent = 0.1

if not gyldig_rabatt:
    print('Ingen rabatt på deg!')
elif pris < 250:
    pris = pris * (1 - rabatt_prosent)
elif pris < 450:
    pris = pris * (1 - rabatt_prosent * 2)
elif pris < 1_000:
    pris = pris * (1 - rabatt_prosent * 2.5)
elif pris < 5_000:
    pris = pris * (1 - rabatt_prosent * 3)
elif pris < 10_000:
    pris = pris * (1 - rabatt_prosent * 3.25)
else:
    pris = pris * (1 - rabatt_prosent * 3.5)

print(f'Sluttsummen er {pris} kroner.')

Du kan ta en titt på den originale koden vi begynte med å prøve å legge til den samme nye betingelsen. Dette er ikke fullt like enkelt! Veldig mye kode som skrives skal forandre seg på et senere tidspunkt. At koden lett kan utvides, sparer derfor tid.

Til slutt er det viktig å forstå at det er mange forskjellige måter man kan skrive om kode på. Det er ingen endelig fasit på hva som er riktig, men retningslinjer for forbedringer. Her er et eksempel som bruker lister fra kapittel 4, løkker fra kapittel 5, og tupler fra kapittel 9 til å omskrive koden:

pris = 500
gyldig_rabatt = True

rabattnivåer = [
    (250, 0.1),
    (450, 0.2),
    (1_000, 0.25), 
    (5_000, 0.3),
    (10_000, 0.325)
]

if not gyldig_rabatt:
    print('Ingen rabatt på deg!')

for nivå, rabatt in rabattnivåer:
    if pris < nivå:
        pris = pris * (1 - rabatt)
        break
else:
    pris = pris * 0.65

print(f'Sluttsummen er {pris} kroner.')

Koden over inneholder flere konsepter du ikke ennå har lært. Likevel tror jeg det er greit for deg å se mangfoldet i hvordan kode kan bli skrevet på. Etter du er ferdig med kapittel 9 kan du komme tilbake her for å lese gjennom koden over mer nøye. Da kan du selv bestemme om du foretrekker dette fremfor koden vi skrev sammen som kun bruker betingede setninger.

3.6 PEP 8 - En standard for god kodestil

I Python er det flere måter å skrive kode på som logisk gjør det samme. Her er tre forskjellige måter å skrive en if-setning:

penger_på_konto = 55_000

if penger_på_konto > 50_000:
    print('Det var mye penger')
    
if penger_på_konto>50_000:
    print ( 'Det var mye penger')
    
if penger_på_konto> 50_000:
  print(   'Det var mye penger'  )

Alle de tre if-setningene over gjør akkurat det samme rent logisk. Likevel har vi litt valg når det kommer til mellomrom på sidene av symbolet > og mellomrom i funksjonen print(). I tillegg har vi variert om kodeblokken i if-setningen er innrykket til høyre med 2 eller 4 mellomrom. Denne fleksibiliteten kan gjøre kode mindre konsekvent og dermed vanskeligere å lese. Hva kan vi gjøre for å bli enige om en felles måte å skrive kode i Python på?

Heldigvis finnes det allerede PEP 8. Dette er retningslinjer for hvordan man skriver Python-kode slik at den blir mer konsekvent. Du kan finne PEP 8 her:

https://peps.python.org/pep-0008/.

Forkortelsen PEP står for Python Enhancement Proposal. Du kan finne en liste over de forskjellige PEP-dokumentene her:

https://peps.python.org/pep-0000/.

Målet med PEP 8 er å gi oss en felles konvensjon for kodestil. Det er ikke et krav å følge PEP 8, men det er likevel ekstremt vanlig. I denne boken skriver jeg kode etter PEP 8-prinsippene, så du vil lære mye om PEP 8 bare ved å følge koden i denne boken. La oss gå gjennom noen eksempler fra PEP 8 sammen for å se hva som står der.

Når du kaller funksjoner i Python, så anbefaler PEP 8 at du ikke har ekstra mellomrom. Den første av de følgende tre linjene er derfor å foretrekke:

print('Det var mye penger')
    
print ( 'Det var mye penger')
    
print(   'Det var mye penger'  )

Både symbolet = for å sette verdier til variabler og sammenligningsoperatorene <, <=, >, >=, ==, != skal ha et enkelt mellomrom på hver side av seg. Det samme gjelder de logiske operatorene and, or, og not.

Det er anbefalt med 4 mellomrom i kodeblokker til if-elif-else-setninger. Det samme gjelder kodeblokker til løkker som vi skal studere i kapittel 5. La oss bruke det vi har sagt så langt til å omskrive kode til å følge PEP 8. Koden vi starter med er som følger:

betalingsmåte=input( 'Skriv inn din betalingsmåte (kort/kontant): ')

if betalingsmåte.lower( )== 'kort' :
    print( 'Starter betaling med kort.')
elif  betalingsmåte.lower()  ==  'kontant':
    print('Starter betaling med kontant.' )
else :
  print( 'Dette er ikke en gyldig betalingsmåte!' )

Dersom vi følger PEP 8-prinsippene kan vi omskrive koden slik:

betalingsmåte = input('Skriv inn din betalingsmåte (kort/kontant): ')

if betalingsmåte.lower() == 'kort':
    print('Starter betaling med kort.')
elif betalingsmåte.lower() == 'kontant':
    print('Starter betaling med kontant.')
else:
    print('Dette er ikke en gyldig betalingsmåte!')

Selv om PEP 8 prøver å standardisere Python-kode, gir den fortsatt rom for noen valg. Dersom en if-setning, elif-setning, eller else-setning bare har en enkelt linje i kodeblokken sin, så kan dette bli skrevet uten å gå ned på en ny linje:

betalingsmåte = input('Skriv inn din betalingsmåte (kort/kontant): ')

if betalingsmåte.lower() == 'kort': print('Starter betaling med kort.')
elif betalingsmåte.lower() == 'kontant': print('Starter betaling med kontant.')
else: print('Dette er ikke en gyldig betalingsmåte!')

Hvilken variant du bruker er avhengig av hvor komplisert linjen i kodeblokken er. I tilfellet over så er det helt fint å ha alt på en enkel linje. Andre ganger blir dette rotete, og du bør heller skrive kodeblokken i if-elif-else-setninger på en egen linje.

Hvis du leser litt i PEP 8, vil du finne anbefalinger for flere konsepter du ennå ikke har lært. Dette gjelder for eksempel lister, løkker, og funksjoner. Likevel kan det å skumlese PEP 8 gi deg et frampek på hva du skal lære om senere. Ta eksempelet med lister. Her kan du lese i PEP 8 hva som er riktig og feil:

# Gyldige måter ifølge PEP 8 å skrive lister
magiske_tall = [4, 8, 15, 16, 23, 42]

magiske_tall = [
    4, 8, 15, 
    16, 23, 42
]

# Ugyldige måter ifølge PEP 8 å skrive lister
magiske_tall = [ 4,8, 15,16, 23,42 ]

magiske_tall = [
  4, 8, 15, 
  16, 23, 42
               ]

Det er helt greit at du ennå ikke vet hva lister i Python er eller hva det kan brukes til. Vi skal kose oss med lister i hele kapittel 4. Det kan likevel være greit å vite hvordan lister skrives i Python ifølge PEP 8-prinsippene.

3.7 Prosjekt - Hvor mye koster en bussbillett?

Nå skal vi gå gjennom et prosjekt i fellesskap for å arbeide med konseptene vi har lært i kapittelet. Du må kode med mens du leser, slik at du får mest mulig ut av prosjektet.

3.7.1 Oppgaven

I dette prosjektet skal vi løse følgende oppgave:

Skriv et program for et busselskap som automatisk regner ut billettprisen til en passasjer. Informasjonen du har tilgjengelig for bussturen er reiselengden, passasjerens alder, og passasjerens studentstatus. Billettprisen skal basere seg på følgende regelsett:

  • En bussbillett skal koste 45 kroner så lenge turen er kortere enn eller lik 30 kilometer. Dersom reisen er lengre enn dette, så skal billetten koste 60 kroner.

  • Personer under 16 år skal få 20 % rabatt på alle billetter.

  • Personer over 65 år skal få 25 % rabatt på alle billetter.

  • Studenter skal ikke betale mer for lengre reiser. En student skal dermed betale det samme for en reise som er 100 kilometer, som for en kort reise.

3.7.2 Et forenklet problem

Vi begynner med å prøve å løse en forenklet versjon av problemet. La oss først se på lengden på reisen uten å ta hensyn til alder eller studentstatus. Koden under løser dette problemet:

lengde_reise = float(input('Hvor mange kilometer er reisen? '))
pris_reise = 0

if lengde_reise <= 30:
    pris_reise = 45
else:
    pris_reise = 60

print(f'Prisen på bussbilletten er {pris_reise} kroner.')

Dette ser jo rimelig rett fram ut. Vi bruker funksjonen input() sammen med funksjonen float() siden kilometer kan være et desimaltall. Etter dette skriver vi en helt vanlig if-else-setning for å sjekke avstanden på reisen. Til slutt skriver vi ut konklusjonen. Nå som vi har kode som kan løse et forenklet problem, så kan vi bygge videre på dette.

3.7.3 Legge på studentstatus

Vi kan nå legge til studentstatus som en ny bit med informasjon vi tar inn. Koden under henter dette inn som en sannhetsverdi og deretter sjekker betingelsen i else-blokken av if-else-setningen.

lengde_reise = float(input('Hvor mange kilometer er reisen? '))
student = bool(int(input('Er passasjeren student? [0 for nei/1 for ja] ')))
pris_reise = 0

if lengde_reise <= 30:
    pris_reise = 45
else:
    if student:
        pris_reise = 45
    else:
        pris_reise = 60

print(f'Prisen på bussbilletten er {pris_reise} kroner.')

Som du kan se fra koden over så ønsker vi at brukeren skal skrive inn 0 dersom passasjeren ikke er en student, og 1 dersom passasjeren er en student. Her gjør vi likevel en ganske spesiell ting: Vi bruker først funksjonen int() og deretter funksjonen bool(). Hvorfor gjør vi det?

Når en passasjer ikke er student så skriver man inn 0. Husk at funksjonen input() tolker alt som strenger, så det er strengen '0' som blir returnert her. Hadde vi brukt funksjonen bool() direkte så ville dette bli evaluert til True ettersom alle ikke-tomme strenger blir evaluert til True. Dette er ikke det vi ønsker. Vi må derfor først konvertere det til heltall slik at vi kun har tallene 0 og 1 å forholde oss til. For heltall så gir funksjonen bool() ut True hvis tallet ikke er 0. Det er denne oppførselen vi vil ha!

Sannhetsverdien student blir deretter brukt inni if-else-setningen til å sjekke om det skal betales ekstra for lange reiser. Nå mangler vi bare informasjon om alder før programmet er ferdig.

3.7.4 Omskrivning av koden

På dette tidspunktet er det fristende å hoppe rett til informasjonen om alder for å bli ferdig. Men det kan være lurt å omskrive programmet etter hvert som man skriver det. Dersom vi kan omskrive programmet til noe enklere vil det å legge til informasjon om alder bli barnemat.

Når vi ser på koden vår, så setter vi først variabelen pris_reise til å være 0. Vi trenger å ha variabelen definert før vi kommer til if-else-setningen, så dette gir mening. Men hva med å sette den til 45 til å begynne med fremfor 0? Hvis vi gjør dette, så kan vi heller skrive koden slik:

lengde_reise = float(input('Hvor mange kilometer er reisen? '))
student = bool(int(input('Er passasjeren student? [0 for nei/1 for ja] ')))
pris_reise = 45

if lengde_reise > 30:
    if student:
        pris_reise = 45
    else:
        pris_reise = 60

print(f'Prisen på bussbilletten er {pris_reise} kroner.')

Dette forenklet koden litt. Men nå er det plutselig enda mer som kan forenkles. Verdien til pris_reise blir kun forandret fra 45 til 60 dersom betingelsene lengde_reise > 30 og not student er begge opprettholdt. Vi kan derfor bruke den logiske operatoren and og skrive følgende kode:

lengde_reise = float(input('Hvor mange kilometer er reisen? '))
student = bool(int(input('Er passasjeren student? [0 for nei/1 for ja] ')))
pris_reise = 45

if lengde_reise > 30 and not student:
    pris_reise = 60

print(f'Prisen på bussbilletten er {pris_reise} kroner.')

Det er enklere å jobbe videre med en enkel if-setning, i stedet for flere nivåer med if-else-setninger.

3.7.5 Legge på alder

Vi kan nå gi rabatt på alder. Dette gjør vi etter den første if-setningen, siden alder kan potensielt gi en prosentrabatt:

lengde_reise = float(input('Hvor mange kilometer er reisen? '))
student = bool(int(input('Er passasjeren student? [0 for nei/1 for ja] ')))
alder = int(input('Hvor gammel er passasjeren? '))
pris_reise = 45

if lengde_reise > 30 and not student:
    pris_reise = 60

if alder < 16:
    pris_reise = pris_reise * 0.8
elif alder >= 65:
    pris_reise = pris_reise * 0.75

print(f'Prisen på bussbilletten er {pris_reise} kroner.')

Koden over løser problemet vårt. Legg merke igjen til nytten av å omskrive koden etter hvert. Vi har endt opp med et sluttresultat som gjør jobben bra, samtidig som den er kortfattet og enkel å utvide.

3.8 Oppgaver

Oppgave 1

Hva gjør følgende kode? Beskriv hvert steg!

avstand = float(input('Hvor mange lysår er reisen mellom planetene? '))
medlem = bool(int(input('Medlem i romfartsklubben? [0 for nei / 1 for ja] ')))
alder = int(input('Hvor gammel er du? '))
billettpris = 5000

if avstand > 60 and not medlem:
    billettpris = 6500

if alder < 12:
    billettpris = billettpris * 0.5
elif alder >= 70:
    billettpris = billettpris * 0.7

print(f'Prisen på romskipbilletten er {billettpris} galaktiske kreditter.')

Sørg spesielt for at du forstår hva som skjer når resultatet fra input() behandles videre med funksjonene int() og bool().

Oppgave 2

I seksjon 3.1 så vi at vi trenger betingede setninger for å håndtere innlogging. La oss lage et veldig enkelt eksempel på dette:

  • Lag en variabel brukernavn og gi den verdien 'eirik'. Lag også en variabel passord og gi den verdien '1234'.

  • Be brukeren om å skrive inn både brukernavn og passord ved hjelp av funksjonen input() to ganger.

  • Sjekk om verdiene brukeren har skrevet inn, stemmer overens med de lagrede verdiene i variablene brukernavn og passord. Hvis brukernavnet og passordet stemmer, skriv ut en melding om at innloggingen er vellykket.

Et ekte innloggingssystem bør kunne håndtere flere brukere, og passord bør ikke lagres i klartekst i koden, da dette utgjør en stor sikkerhetsrisiko. Likevel er logikken vi har skrevet her grunnlaget for et standard innloggingssystem.

Oppgave 3

I denne oppgaven skal du skrive et program som sjekker om et årstall som brukeren oppgir, er et skuddår. Reglene for skuddår er:

  • Et år er et skuddår hvis det er delelig med 4, slik som 2020 og 2024.

  • Unntak: År som er delelige med 100, slik som 1800 og 1900, er ikke skuddår.

  • Unntak fra unntaket: År som er delelige med 400, slik som 2000 og 2400, er likevel skuddår.

Be brukeren om å skrive inn et årstall ved hjelp av funksjonen input(). Skriv ut en tilbakemelding i terminalen som forteller om årstallet er et skuddår eller ikke. Du må bruke modulus-operatoren % vi lærte om i seksjon 1.4 for å sjekke om året er delelig på 4, 100, og 400.

Oppgave 4

Rett koden under slik at den er i tråd med PEP 8-prinsippene for kodestil:

#brukeren skriver inn alderen sin
alder=input('Hvor gammel er du? ')

if int(alder)>=18:
  print ( 'Du er myndig!' )
else :
    print(  'Du er ikke myndig ennå.' )

Oppgave 5 (Utfordrende)

Noen if-else-setninger er relativt enkle og endrer bare verdien av en enkelt variabel. Her er et eksempel:

smertenivå = 7
kritisk = ''

if smertenivå > 8:
    kritisk = 'Ja'
else:
    kritisk = 'Nei'

I Python kan vi skrive slike if-else-setninger på en enkelt linje slik:

smertenivå = 7
kritisk = 'Ja' if smertenivå > 8 else 'Nei'

Det er en kompakt måte å skrive if-else-setninger som bare endrer en enkelt variabel. Bruk denne måten å skrive enkle if-else-setninger på for å forenkle koden i prosjektet vi laget i seksjon 3.7. Er det alltid lurt å forenkle kode på denne måten? Hvordan påvirker dette leseligheten?