Kapittel 2 Grunnleggende datatyper
Supert at du kom deg gjennom det første kapittelet! På mange måter er det første kapittelet det vanskeligste dersom du er ny innen programmering. I dette kapittelet skal vi lære mer om datatyper. En god forståelse av datatyper er helt nødvendig for å skrive mer avanserte programmer i Python. I tillegg skal vi også se på hvordan du kan be om informasjon fra brukerne av programmene dine. Dette vil gjøre programmene dine mer interaktive og nyttige.
2.1 Introduksjon - Hva er greien med datatyper?
Før vi lærer om forskjellige datatyper i Python må vi besvare to fundamentale spørsmål:
Hva er datatyper?
Hvorfor er datatyper nyttig?
I Python, og i de fleste andre programmeringsspråk, er datatyper16 en indikasjon av hvilken type informasjon noe er. Vi har sett eksempler med datatypen heltall som tallene 1, 17, og -137. Et annet eksempel er strenger som er biter med tekst som 'Heisan!' og 'Ring 113 hvis du trenger medisinsk hjelp'. I Python trenger vi ikke eksplisitt si hvilken datatype en variabel har, men kan bare skrive:
Her forstår Python at variabelen navn holder en streng, mens variabelen alder holder et heltall. Andre programmeringsspråk er mer strenge på dette, og vil kreve at vi eksplisitt skriver hvilken datatype vi oppretter.
Vi kan også representere tallet 26 som en streng ved å bruke apostrofer '' slik:
Hvilken forskjell har det om 26 blir representert som et heltall eller en streng? Et eksempel er at datatyper oppfører seg forskjellig med hensyn på operasjoner. Hva tror du koden under vil gi oss?
Intuitivt kan man kanskje forvente at 29 blir skrevet ut to ganger. Men slik er det ikke! For heltall så beskriver + vanlig addisjon, mens for strenger så slår symbolet + sammen strengene. Derfor blir 263 skrevet ut til slutt. Denne oppførselen kan være nyttig dersom vi ønsker å legge til en landskode på et telefonnummer:
Her ønsker vi å legge til landskode til starten av telefonnummer. Dermed er bruk av strenger det korrekte valget. Hva hadde skjedd dersom du hadde representert variablene som heltall? Da hadde du bare lagt sammen tallene 47 og 46 90 43 23 med vanlig addisjon, som hadde gitt deg 46 90 43 70. Ikke særlig nyttig for denne applikasjonen.
En god forståelse av datatypene i Python vil hjelpe deg å skrive effektive programmer. Det er overraskende hvor ofte riktig valg av datatype kan hjelpe med et problem. På den andre siden er det også svært vanlig at feil i programmer skyldes dårlig valg av datatyper.
I dette kapittelet skal vi lære om noen av de vanligste datatypene i Python, og hvordan jobbe med dem. Senere i boken vil vi støte på nye datatyper som er mer kompliserte, men likevel bygger på de samme prinsippene.
2.2 Numeriske datatyper
I Python er det tre innebygde numeriske datatyper: heltall, flyttall, og komplekse tall. Komplekse tall17 er langt mindre brukt enn de to andre datatypene og bygger på litt avansert matematikk. Derfor vil vi ikke beskrive komplekse tall i denne boken. La oss gå gjennom heltall og flyttall individuelt først, og deretter snakke om hva som gjelder for dem i fellesskap etterpå.
2.2.1 Heltall
Heltall18 er den enkleste numeriske datatypen i Python, og brukes til å representere ordinære tall, både positive og negative, som ikke har en desimalkomponent. Her er tre eksempler:
I det tredje eksempelet ovenfor så har jeg lagt inn en understrek _ i tallet 85_230. Dette endrer ingenting, men gjør store tall mer leselige for oss i Python. Det er ikke krise å skrive 85230 uten understrek, men tall som 3000000000 er mye vanskeligere å lese enn 3_000_000_000.
Et viktig konsept er at du kan skrive tall i andre tallsystemer enn i titallsystemet. Du har kanskje tidligere vært borti totallsystemet, eller binærtall19, som det også kalles. I titallsystemet har man alle 10 symbolene 0, 1, 2, …, 8, og 9 å rutte med. I totallsystemet så har man derimot bare 2 symboler, nemlig 0 og 1. I titallsystemet benytter vi en ny posisjon når vi har brukt opp alle symbolene, slik at det blir \(\dots 7, 8, 9, 10, 11, 12, \dots\). Vi gjør det samme med binærtall, bare at vi kun har to symboler å bruke. Dermed blir de første ti tallene våre skrevet slik som binærtall:
| Titallsystemet | Binærtall | Binærtall i Python |
|---|---|---|
| 0 | 0 | 0b0 |
| 1 | 1 | 0b1 |
| 2 | 10 | 0b10 |
| 3 | 11 | 0b11 |
| 4 | 100 | 0b100 |
| 5 | 101 | 0b101 |
| 6 | 110 | 0b110 |
| 7 | 111 | 0b111 |
| 8 | 1000 | 0b1000 |
| 9 | 1001 | 0b1001 |
Datamaskiner opererer med elektriske signaler som tolkes som to ulike tilstander: lav spenning (0) eller høy spenning (1). Dette er grunnen til at binærtall er så naturlig for datamaskiner.
Som du kan se av tabellen over legger vi på symbolene 0b for å representere binærtall i Python:
Det er naturlig å tenke at binærtall er en egen datatype i Python. Dette stemmer ikke. Binærtall er bare en annen representasjon av det samme tallet. Så Python lagrer binærtallet 0b110 på helt lik måte som tallet 6. Derfor er akkurat de samme operasjonene på binærtall gyldige på samme måte som for heltall i titallsystemet. Du kan addere binærtall slik:
første_binærtall = 0b110
andre_binærtall = 0b1001
print('Summen av tallene er lik:', første_binærtall + andre_binærtall)Som du kan se av programmet over så gjør Python om heltall til titallsystemet når de blir skrevet ut. Siden 110 i binær er 6 i titallsystemet, og 1001 i binær er 9 i titallsystemet, så blir summen 15 skrevet ut.
Hva om du ikke stoler på meg? Er det en måte du kan være sikker på at binærtall har den samme datatypen som vanlige heltall? Du kan bruke funksjonen type() i Python for å sjekke hvilken datatype noe er. Koden under sjekker datatypen til binærtall og vanlige heltall i Python:
heltall_titallsystemet = 9
heltall_binær = 0b1001
print('Datatypen til heltall i titallsystemet:', type(heltall_titallsystemet))
print('Datatypen til heltall i binær:', type(heltall_binær))Som du kan se av utskriften har begge tallene datatypen <class 'int'>. Dette er Pythons måte å si at datatypen er heltall.
2.2.2 Flyttall
Flyttall20 er datatypen i Python som representerer desimaltall. Her er tre eksempler:
I Python brukes symbolet . som skillet mellom heltallsdelen og desimaldelen av et flyttall. Du er kanskje vant til å bruke komma , som desimalskille i matematikkundervisningen, men punktum . brukes i nesten alle programmeringsspråk.
Når vi jobber med desimaltall, har vi ofte behov for å forenkle resultatet. For eksempel: Hvis du betaler kontant for havregryn til 17.99 kroner, må du i praksis betale 18 kroner siden vi ikke lenger bruker øre-mynter. I Python kan du avrunde til nærmeste heltall ved å bruke den innebygde funksjonen round() som følger:
Hvis du skriver ut variablene kostnad_havregryn_kontant og dårlig_approksimasjon_av_pi over, vil du se at det er heltallene 18 og 3 som blir skrevet ut til terminalen. Det å avrunde den matematiske konstanten \(\pi\) ned til tallet 3 er sjelden lurt, så pass på at avrundinger gir mening i situasjonen du jobber med.
Det er ofte naturlig å kombinere heltall og flyttall ved å bruke en operasjon. Et enkelt eksempel kan være å legge sammen to kostnader der den ene er et heltall, og den andre er et flyttall:
kostnad_havregryn = 17.99
kostnad_mobilabonnement = 449
sum_av_kostnader = kostnad_havregryn + kostnad_mobilabonnement
print('Summen av kostnadene er:', sum_av_kostnader)Hvis du kjører koden over vil du se at tallet 466.99 blir skrevet ut. Så summen av et flyttall og et heltall er et flyttall. Gir dette mening? Ja! Hadde summen vært et heltall, så mister vi informasjonen om 99 øre. Dette gir kanskje mening med kontantbetaling, men ikke med betaling med kort der ører blir registrert på lik linje med kroner. Vi kan alltids konvertere sluttresultatet til et heltall ved å bruke round() funksjonen om vi ønsker etterpå.
Her støter vi på et generelt prinsipp: Python vil foretrekke å ikke miste informasjon når operasjoner tar sted mellom forskjellige datatyper. Derfor blir summen av et heltall og et flyttall igjen et flyttall, slik at desimaltallene kan bli beholdt. Du kan selv teste at de samme skjer med andre operasjoner som subtraksjon, multiplikasjon, og divisjon.
2.2.3 Hvordan konvertere mellom heltall og flyttall?
Vi har sett at den innebygde funksjonen round() kan brukes til å avrunde et flyttall til et heltall. Denne funksjonen vil avrunde slik du lærte om på barneskolen. Så flyttallene 31.3, 22.6 og 17.7 vil bli avrundet til heltallene 31, 23, og 18 når du bruker funksjonen round().
Hva om du ønsker å bare fjerne desimaldelen uten å avrunde? Du ønsker altså å konvertere 31.3, 22.6, og 17.7 til heltallene 31, 22, og 17. Da kan du bruke funksjonen int(). Denne funksjonen vil bare fjerne desimaldelen av et flyttall. Funksjonen int() kan ikke bare bli brukt på flyttall, men også på strenger som eksempelet under viser:
Programmet over konverterer først '5' og '3' til heltall, og etterpå adderer dem. Derfor vil tallet 8 bli skrevet ut. Det er ikke alltid at funksjonen int() vil kunne konvertere en streng til et heltall. Koden under vil gi en feilmelding siden det ikke er noen naturlig måte å konvertere strengen 'Heisan!' til et heltall:
Når du kjører koden over får du en ValueError. Dette er en feil som indikerer at datatypen du bruker er gyldig, men at verdien likevel ikke er gyldig. I vårt tilfelle så kan funksjonen int() benyttes på strenger, men ikke alle strenger. Så int('3') vil kjøre helt fint, mens int('Heisan!') vil gi en ValueError.
Hva med å gå motsatt, altså å konvertere et heltall til et flyttall? Hvilket flyttall bør for eksempel tallet 3 bli transformert til? Vi vet at matematisk så er \(3 = 3.0\). Så en konvertering mellom heltall og flyttall bør bare legge på 0 etter desimaltegnet. Det er nettopp dette funksjonen float() gjør:
Kjører du koden over så ser du at funksjonen float() kan brukes til å konvertere en streng til et flyttall dersom dette gir mening.
Hvis vi tar et steg tilbake ser vi at både heltall og flyttall har sine egne funksjoner int() og float() for å konvertere andre datatyper til sitt format. Dette er ikke alltid mulig som vi så med eksempelet int('Heisan!'). Likevel er det veldig nyttig å kunne konvertere mellom datatyper. De fleste andre datatypene vi kommer til å møte i boken har egne funksjoner for konvertering knyttet til seg slik som int() er knyttet til heltall og float() er knyttet til flyttall.
2.2.4 Hva er felles for heltall og flyttall?
Selv om heltall og flyttall er forskjellige datatyper i Python har de også mye til felles.
Det er alltid mulig å konvertere et heltall til et flyttall og tilbake igjen med funksjonene
int()ogfloat(). Vi så i forrige seksjon at det ikke alltid er mulig å konvertere en streng til et heltall.De vanlige operasjonene addisjon, subtraksjon, multiplikasjon, og divisjon gir alltid mening mellom heltall og flyttall. Dette er ikke sant mellom strenger og heltall siden koden
'Heisan' + 5vil gi enTypeError. EnTypeErrorindikerer at en operasjon eller handling ikke er gyldig på grunn av datatypene som er med. Dette er en veldig vanlig feil og er en sterk indikasjon på at du bruker datatyper feil.Det er andre innebygde funksjoner i Python som kan behandle både heltall og flyttall. Et eksempel er den innebygde funksjonen
abs()som tar absoluttverdien av et tall. Dette vil si at positive tall forblir uendret, mens negative tall blir flippet til positive. Såabs(5)gir heltallet5, mensabs(-3)gir heltallet3. Funksjonenabs()kan også bli brukt på flyttall. Såabs(3.5)gir flyttallet3.5, mensabs(-7.32)gir flyttallet7.32. Du vil derimot få enTypeErrorhvis du prøver å kjøre kodenabs('Heisan!').
Siden heltall og flyttall er såpass like så er det ekstra viktig å holde orden på hva som er hva i koden din. La oss nå gå over til datatypen som representerer tekst, nemlig strenger. Selv om vi allerede har diskutert strenger litt så er det mye mer vi kan gjøre med strenger enn det du har sett så langt.
2.3 Strenger
De fleste programmer behandler tekst på en eller annen måte. Her er to eksempler:
Et videospill har menyer med tekstalternativer og har ofte dialoger mellom karakterer i spillet. I en del spill kan også spilleren påvirke hvordan dialogen utfolder seg ved å velge dialogalternativer. Hvis du tenker deg om er det nok vanskelig å komme på et videospill som ikke behandler tekst nesten overalt.
Et program som automatisk sender epost ved gitte tidsintervaller til en stor kundebase må behandle innholdet i epostene. Når jeg får en epost fra et internasjonalt selskap som begynner med “Dear Eirik”, så vet jeg at tekstbehandling i et programmeringsspråk har blitt benyttet for å sette nettopp mitt navn inn her. At en representant i selskapet har lagt meg til med kjærlig omhu stemmer nok neppe.
Siden tekst er overalt, skal vi ta oss litt tid til å bli stødig på behandling av tekst.
2.3.1 Opprettelse av strenger
La oss ta det fra begynnelsen: En streng21 er en serie med karakterer i Python. Strenger har typen str i Python. Hvis du skriver ut type('Hallo') til terminalen vil Python svare med <class 'str'>.
Karakterene i strenger kan være bokstaver, tall, eller andre symboler:
postnummer = '0489'
beskjed = "Det er ikke trygt å ferdes alene"
sensurert = 'Personnummer: 382934 #####'Som du kan se fra eksemplene over så kan du enten bruke apostrofer ' eller hermetegn " når du definerer strenger. Det er viktig at du bruker samme tegnet, enten ' eller ", på både starten og slutten av en streng.
Hva skjer hvis du ønsker å ha symbolet ' eller " i selve teksten? Her må du være litt forsiktig slik at du ikke ender strengen for tidlig. Koden under vil krasje siden strengen blir endt allerede etter to ord:
Dersom du kjører koden over vil du få en SyntaxError. Dette er en feil som indikerer at syntaksen, altså måten symbolene er arrangert på, ikke er riktig. En syntaksfeil tyder ofte på at det er for få eller for mange apostrofer ' eller hermetegn " i en streng.
En enkel måte å løse dilemmaet vårt på er å bruke apostrofer når hermetegn skal brukes inni strengen og omvendt:
2.3.2 Unntakssymboler
Når tekster er veldig lange, som en hel bok, så inneholder de ofte både apostrofer og hermetegn. I slike tilfeller kan du bruke et skråstreksymbol foran symbolene, slik at du skriver \' og \". Da forteller du Python at her skal symbolene ' og " tolkes som ren tekst uten noen logisk betydning for programmet:
utdrag_fra_bok = "Han sa \"Du er en idiot\" til meg"
utdrag_fra_annen_bok = 'Det er Gunnar\'s sykkel'Vi kaller \' og \" for unntakssymboler22. Det finnes mange unntakssymboler i Python som hjelper oss med å behandle strenger. Et annet eksempel er unntakssymbolet \n for å lage en ny linje i teksten:
Kjører du koden over vil det kjente diktet komme på riktig form med en ny linje per frase. En annen måte å lage en streng som strekker seg over flere linjer er å bruke tre symboler på hver side av strengen, og deretter bare trykke enter-tasten når du vil ha linjeskift:
mauren_dikt = """
Liten?
Jeg?
Langtifra.
Jeg er akkurat stor nok.
Fyller meg selv helt
på langs og på tvers
fra øverst til nederst.
Er du større enn deg selv kanskje?"""Dette kalles naturlig nok en flerlinjet streng23. Når du bruker tre symboler på hver side av strengen, enten ''' eller """, så trenger du ikke bruke \n for å få en ny linje. Vi skal spesielt bruke flerlinjede strenger i kapittel 6 når vi skriver dokumentasjonsstrenger til funksjoner.
Som du merker er det ofte flere måter å løse et problem på i Python. Denne fleksibiliteten er en styrke, og det er som regel fordeler og ulemper med hvert av valgene.
2.3.3 Lengden til strenger
Noen strenger er korte, som et fornavn eller et akronym. Andre strenger er lengre, som en paragraf fra lovdata. En sentral egenskap til en streng er lengden til strengen. Dette er målt i antall karakterer strengen inneholder. For å finne lengden til en streng kan du bruke den innebygde funksjonen len():
fornavn = 'Eirik'
utdrag_mauren_dikt = 'Liten?\nJeg?\nLangtifra.\nJeg er akkurat stor nok.'
print(len(fornavn))
print(len(utdrag_mauren_dikt))Legg merke til at funksjonen len() teller absolutt alt: bokstaver, punktum, mellomrom og til og med unntakssymbolet \n teller som én karakter.
Det er lett å lage strenger som har lengde 1, 7, eller 17. Men hva med lengde 0? Dette kan vi også lage som eksempelet under viser:
Dette kalles den tomme strengen24. Den inneholder ingen karakterer. Dette virker kanskje litt merksnodig. Hvorfor skal vi bruke en slik streng? Vi skal i seksjon 2.6 lære om hvordan du tar inn informasjon fra en bruker som kjører programmet ditt. Dersom brukeren ikke skriver inn noe så vil du ende opp med den tomme strengen. Så selv om vi ikke lager tomme strenger for moro skyld, dukker de ofte opp i programmer.
2.3.4 Sammenslåing av strenger
La oss se litt på hvordan vi kan slå sammen strenger. En måte å gjøre dette på er å bruke tegnet + slik:
navn = 'Kari'
introtekst = 'Velkommen til vår tjeneste, '
fullstendig_tekst = introtekst + navn + '!'
print(fullstendig_tekst)Koden over printer ut 'Velkommen til vår tjeneste, Kari!'. Dersom du endrer variabelen navn til et annet navn som Anette, så vil beskjeden forandre seg til 'Velkommen til vår tjeneste, Anette!'. Dette er den magiske og litt irriterende grunnen til at du får personlige e-poster fra nettbutikker.
Du kan ikke direkte slå sammen en streng og et heltall:
Koden her vil krasje hvis du kjører den og gi en TypeError. Det er ikke mulig å addere en streng og et heltall. Her ønsker vi selvsagt å behandle løsning som en streng og legge den sammen med introtekst. Vi trenger altså å konvertere et heltall til en streng. Hvis du har et heltall eller et flyttall, så kan du bruke funksjonen str() for å konvertere dem til strenger. Koden under gjør dette, og dermed ordner feilen ovenfor:
Å bruke symbolet + til å slå sammen strenger går fint i enkle tilfeller. Se likevel hvor fort koden blir vanskelig å lese:
forsvar = 4
midtbane = 4
angrep = 2
print(str(forsvar) + '-' + str(midtbane) + '-' + str(angrep) + ' formasjonen.')Det som blir skrevet ut er mye mer leselig enn selve koden. Koden er stappet full av +-symboler og funksjonen str(). Et bedre alternativ her er formaterte strenger25. La meg først vise deg hvor enkelt det da vil se ut, og deretter kan vi diskutere det i mer detalj:
Dette ble plutselig mye enklere å lese! Formaterte strenger har som du ser symbolet f rett før strengen åpner. Dette indikerer at vi skal skrive en formatert streng. I formaterte strenger kan du sette inn variabler direkte, så lenge du bruker krøllparentesene { og } rundt variablene. Variablene blir også automatisk konvertert til strenger. Så det er ikke behov for +-symbolet eller funksjonen str().
Jeg er en stor tilhenger av formaterte strenger. I mer kompliserte eksempler er formaterte strenger nesten alltid enklere å lese. For enkle eksempler kan du velge om du ønsker å bruke + symbolet eller formaterte strenger:
navn = 'Kari'
introtekst = 'Velkommen til vår tjeneste, '
tekst_med_addisjon = introtekst + navn + '!'
tekst_med_formaterte_strenger = f'{introtekst}{navn}!'Det er viktig at du forstår begge skrivemåtene. Selv om du foretrekker formaterte strenger, vil du garantert møte mye kode skrevet av andre som bruker symbolet + for å slå sammen tekst.
2.4 Metoder - Funksjoner som hører til datatyper
Så langt har vi lært om datatypene heltall, flyttall, og strenger. La oss ta et avbrekk fra å lære nye datatyper for å lære litt om et utrolig viktig konsept, nemlig metoder26. Den forenklede definisjonen er at metoder i Python er funksjoner som hører til en spesiell datatype. Hva betyr dette?
Vi har så langt brukt funksjoner som for eksempel print() og type(). Dette er innebygde funksjoner som kan ta inn forskjellige datatyper. Metoder hører derimot til en datatype og blir skrevet på en litt annen måte. Et eksempel er metoden .capitalize() som hører til strenger:
setning = 'hei, hvordan går det med deg?'
setning_med_stor_bokstav = setning.capitalize()
print(setning_med_stor_bokstav)Metoden .capitalize() tar den første bokstaven i strengen setning og gjør den om til en stor bokstav. Som du kan se av koden over så benyttes metoden .capitalize() ved å skrive setning.capitalize(). Her virker metoden .capitalize() på variabelen setning som representerer en streng. Metoder blir brukt med punktum-notasjonen, framfor funksjoner som ikke tilhører en datatype slik som len(setning).
Hva skjer dersom du prøver å bruke metoden .capitalize() på et heltall?
Hvis du kjører koden over vil du få en AttributeError. Dette er en feil som indikerer at datatypen heltall ikke har noen metode med navnet .capitalize(). Siden .capitalize() hører til strenger kan vi derfor ikke bruke den på heltall.
Dette kan virke litt forvirrende med første øyekast. Hører ikke også len() funksjonen til strenger? Funksjonen len() kan faktisk brukes på mange datatyper. Vi skal for eksempel i kapittel 4 bruke funksjonen len() på lister. Distinksjonen mellom metoder og selvstendige funksjoner er litt vanskelig i begynnelsen. Per nå er det best om du bare aksepterer at noen funksjoner hører til datatyper, og at da blir notasjonen med punktum brukt slik som i setning.capitalize(). I kapittel 11 vil vi gå gjennom metoder mer nøyaktig, så det er bare å glede seg.
La oss se noen flere eksempler på metoder for å bli komfortabel med konseptet. For strenger eksisterer det en drøss med metoder som transformerer og analyserer innholdet i strenger. Her er noen av mine favoritter:
stadiumrop = 'Heia brann!'
stadiumrop_store_bokstaver = stadiumrop.upper()
print(stadiumrop_store_bokstaver)
utsagn = 'Jeg elsker epler. Uten tvil er epler min favorittfrukt!'
antall_epler = utsagn.count('epler')
print(f'Det er {antall_epler} epler i utsagnet.')
telefonnummer = '48394633'
er_bare_tall = telefonnummer.isdigit()
print(er_bare_tall)Metoden .upper() tar en streng og gjør den om til store bokstaver. Metoden .count() teller antall ganger en bit med tekst gjentar seg i en større tekst. Vi sjekker hvor mange ganger ordet 'epler' gjentar seg i variabelen utsagn. Her ser du at metoder iblant også kan ta inn argumenter, som 'epler' i dette tilfellet.
Til slutt bruker jeg metoden .isdigit() for å sjekke om telefonnummer bare har tall i seg. Som du ser hvis du kjører koden så blir True skrevet ut. Verdien True er ikke en streng, men en sannhetsverdi. Dette er en datatype vi skal se nærmere på i neste seksjon.
Det er ikke bare strenger som har metoder tilgjengelig. Et enkelt eksempel på en metode som hører til flyttall er .is_integer(). Denne sjekker om et flyttall er praktisk talt et heltall, altså om flyttallet er på formen \(1.0\), \(-5.0\), \(17.0\), og så videre.
pris_lenestol = 499.99
print(pris_lenestol.is_integer())
pris_skrivebord = 2000.0
print(pris_skrivebord.is_integer())Når du kjører koden over vil den først skrive ut False og deretter True. Dette er de to sannhetsverdiene vi snart skal se mer på.
Noen datatyper som flyttall har relativt få metoder, mens andre datatyper som strenger har veldig mange metoder. Metoder gir oss ofte nyttig funksjonalitet. Vi skal i løpet av boken bruke mange metoder på forskjellige datatyper.
2.5 Sannhetsverdier og NoneType
I denne seksjonen skal vi studere to datatyper, nemlig sannhetsverdier27 og NoneType. Grunnen til at vi går gjennom begge datatypene i samme seksjon er at de har noe til felles: Selv om du kan definere dem eksplisitt i koden din, så er det mest vanlig at sannhetsverdier og NoneType dukker opp indirekte. La oss begynne med sannhetsverdier som vi allerede har tatt en rask titt på i forrige seksjon.
2.5.1 Sannhetsverdier
Sannhetsverdier i Python er representert med nøkkelordene True og False. Verdiene indikerer om en betingelse er opprettholdt. Som du sikkert forstår så representerer True at betingelsen er opprettholdt, mens False representerer at betingelsen ikke er opprettholdt. Eksempelet under viser hvordan sannhetsverdier kan settes i Python:
Vi bruker sannhetsverdier til å gjøre forskjellige handlinger basert på om verdien er True eller False. I hele kapittel 3 er sannhetsverdier sentralt, så du vil jobbe mye med True og False. La oss allerede her gi et lite frempek på hva vi kan bruke sannhetsverdier til.
Sett at du skal skrive kode til en nettbutikk for en bedrift som produserer og leverer strikkegensere. Det er ønskelig at kjøpere må betale frakt på 10 %. Men dersom det blir handlet strikkegensere for over 1000 kroner skal frakten være gratis. Hvordan kunne vi skrevet kode for å automatisk gjøre dette? Et ærlig forsøk basert på det vi allerede har lært kan se slik ut:
total_kjøpesum = 750
frakt = 0.1 * total_kjøpesum
print(f'Frakten for denne kjøpesummen er {frakt} kroner.')Koden over setter variabelen frakt til å være 10 % av verdien til total_kjøpesum. Så langt, så godt! Men dersom en ny bruker handler for 1250 kroner, så vil koden se slik ut:
total_kjøpesum = 1250
frakt = 0.1 * total_kjøpesum
print(f'Frakten for denne kjøpesummen er {frakt} kroner.')Dette blir ikke riktig, siden nå burde verdien til frakt være 0 siden det blir handlet for over 1000 kroner. Vi ønsker at programmet vårt skal oppføre seg forskjellig basert på om total_kjøpesum er over eller under verdien 1000. I kapittel 3 skal vi lære å skrive koden under:
total_kjøpesum = 1250
if total_kjøpesum < 1000:
frakt = 0.1 * total_kjøpesum
else:
frakt = 0
print(f'Frakten for denne kjøpesummen er {frakt} kroner.')Prøv å kjøre koden over der du setter forskjellige tallverdier til variabelen total_kjøpesum. Dersom du endrer variabelen total_kjøpesum til å ha verdien 750 så vil frakt bli 75.0. Så her oppdaterer frakten seg basert på om total_kjøpesum er over eller under 1000 kroner. Dette kalles en betinget setning28. Her er det betingelsen total_kjøpesum < 1000 som evalueres.
Hvordan henger betingede setninger sammen med sannhetsverdier? Utrykket total_kjøpesum < 1000 i koden over er en sammenligning mellom to verdier. Hvis du kjører følgende kode under, hva tror du at du får da?
Som du kan se hvis du kjører koden så blir verdien False skrevet ut. Dette gir mening, siden 1250 ikke er mindre enn 1000. Så i betingede setninger som vi så ovenfor så er sannhetsverdier hyppig brukt. Vi skal som sagt bruke hele kapittel 3 på betingede setninger, så vi plukker opp igjen denne tråden der.
2.5.2 NoneType
I Python er det et reservert nøkkelord med navnet None. Dette er en verdi av typen NoneType som variabler kan ta:
Hva i all verden skal dette representere? I Python brukes verdien None til å signalisere mangelen på informasjon, eller at en tilstand er tom. Så koden over vil indikere til en leser at antall beboere i Agder er ukjent for den som skrev koden på det tidspunktet. Kanskje informasjonen ikke var lett tilgjengelig? Kanskje personen som skrev koden ventet på nye oppdateringer fra en folkeopptelling? Uansett grunn indikerer None her en mangel på informasjon. Akkurat som med sannhetsverdier så er det mindre vanlig at du selv vil definere en variabel til å ta verdien None.
Et annet eksempel på forekomst av None er ved bruk av smarte temperatursensorer. La oss anta at du har en smart temperatursensor i stuen din som måler hvor varmt det er en gang i timen. Så for hver time blir det sendt et flyttall som for eksempel 21.3 eller 19.5 til mobilen din. Men dersom en feil skjer med sensoren, og den greier ikke å måle temperaturen, så vil den typisk sende verdien None. Dette indikerer at denne verdien mangler. Det betyr ikke at temperaturen ikke eksisterte på det tidspunktet. Stuen din hadde åpenbart en eller annen temperatur da. Verdien None indikerer bare at vi mangler informasjon fra den smarte temperatursensoren.
Et annet sted verdien None dukker opp er i sammenheng med funksjoner. Vi har sett at en del funksjoner som abs() og str() returnerer en ny verdi. Så i koden absoluttverdi = abs(-3.5) vil returverdien 3.5 bli lagret i variabelen absoluttverdi. Men ikke alle funksjoner returnerer en verdi. Et eksempel er funksjonen print(). Den skriver ut til terminalen, men returnerer ingenting som kan bli lagret i en variabel. Dette kan vi enkelt sjekke med følgende kode:
Hvis du kjører koden over så vil to linjer bli skrevet ut til terminalen. Den første er 'Heisann og hopsann og fallerallera', som kommer fra funksjonen print() i første linje i koden. Den andre linjen som blir skrevet ut er None. Variabelen returverdi har verdien None fordi funksjonen print() ikke har definert en returverdi. Slike funksjoner returnerer None, som er en indikasjon på at det ikke finnes informasjon å hente i returverdien.
Vi skal i kapittel 6 lære å definere våre egne funksjoner. Da kan vi velge om funksjonene skal returnere None eller en annen datatype. Per nå trenger du bare vite at datatypen NoneType eksisterer med None som eneste mulige verdi. Verdien None representerer at informasjon mangler.
Vi er nå ferdig med å se på de fem datatypene heltall, flyttall, strenger, sannhetsverdier, og NoneType i første omgang. Resten av dette kapittelet går ut på å ta inn verdier fra en bruker av programmet. Vi vil se at en god forståelse av datatyper gjør oss langt mer trygge på hva vi holder på med.
2.6 Ta inn informasjon fra en bruker
Så langt har programmene våre ikke kommunisert med omgivelsene. Dette begrenser i stor grad hvor komplekse programmene våre kan være. Hvis vi ser på programmer vi selv bruker så er det et viktig aspekt vi mangler:
Når vi spiller et videospill så trykker vi på taster for å bevege karakteren vi styrer.
Når vi fører timer i et regnskapssystem så skriver vi inn hvor mange timer vi har brukt på en oppgave.
Når vi bruker sosiale medier skriver vi kommentarer om hvor mye duftlys har beriket livene våre.
De tre eksemplene over har en ting til felles: Brukerne av programmene gir selv informasjon til programmet!
I Python er det mange måter å ta inn informasjon fra en bruker. Den enkleste, som vi skal lære nå, er å bruke funksjonen input(). Dette er en funksjon som lar brukeren skrive inn informasjon i terminalen.
Programmer som dataspill, regnskapssystemer, og sosiale medier har mer avanserte måter å ta inn informasjon på. I tillegg har de mer brukervennlighet og visuell eleganse enn det man finner i et terminalvindu. Likevel er det mange terminalapplikasjoner som brukes av andre programmerere, systemadministratorer, og andre tekniske personer. Å ta inn informasjon fra en bruker i terminalen er derfor et godt sted å begynne.
Prøv å kjøre programmet under:
Når du kjører programmet så vil funksjonen input() stå og vente på at du skriver inn noe i terminalen. Skriv inn en hyggelig beskjed og trykk deretter tasten enter. Når du gjør dette vil beskjeden bli lagret i variabelen beskjed. Dette blir til slutt skrevet ut tilbake til deg med funksjonen print().
Det første vi bør merke oss er at programmet er lite intuitivt. Hvordan skal brukeren av programmet vite hva de skal skrive inn? I funksjonen input() kan vi legge inn en streng som beskriver hvilken informasjon vi er ute etter fra brukeren. Hvis du kjører programmet under så vil du få litt mer veiledning:
Nå er programmet mer forståelig, men kanskje ikke særlig nyttig. Ta heller en titt på programmet under:
fornavn = input('Skriv inn fornavnet ditt: ')
etternavn = input('Skriv inn etternavnet ditt: ')
fornavn = fornavn.capitalize()
etternavn = etternavn.capitalize()
fullt_navn = fornavn + ' ' + etternavn
print(f'Ditt fulle navn er {fullt_navn}.')Programmet bruker funksjonen input() to ganger for å be om både fornavnet og etternavnet til brukeren. Deretter kan vi bruke strengmetoden .capitalize() for å sikre at både fornavnet og etternavnet begynner på en stor bokstav. Vi lager til slutt det fulle navnet til brukeren ved å sette sammen strengene med et mellomrom i midten.
Selv om dette eksempelet er veldig grunnleggende, har du sikkert møtt lignende registreringssystemer. Et eksempel er registrering på forskjellige nettsider der de tvinger deg til å skrive en gyldig epost-adresse og et sterkt passord. Dette bygger på det samme prinsippet som ovenfor, bare med litt mer involvert kode for å sjekke at det brukeren skriver inn er gyldig. Ikke undervurder verdien av enkel kode!
2.7 Prosjekt - Hvor gammel er hunden din i hundeår?
Nå er det tid for å gå gjennom et prosjekt. Prosjektet kommer til å forsterke kunnskapen du har opparbeidet deg i løpet av kapittelet. For å få mest mulig ut av prosjektene bør du kode samtidig som du leser.
2.7.1 Oppgaven
I dette prosjektet skal vi løse følgende oppgave:
Skriv et program som hjelper brukere med å finne ut hvor mange hundeår hunden deres er, gitt at de vet hvor mange menneskeår hunden er.
2.7.2 Forberedelse
Før vi starter å kode er det lurt å mentalt tenke over hva vi trenger:
Vi trenger å hente inn informasjon om hundens alder i menneskeår fra brukeren.
Vi trenger å gjøre om fra menneskeår til hundeår.
Vi trenger å skrive ut til terminalen hvor gammel hunden er i hundeår.
For oppgaven kommer vi til å operere med følgende konvertering mellom hundeår og menneskeår: Hvis \(H\) representerer hundeår og \(M\) representerer menneskeår så bruker vi formelen:
\[H = 7 \cdot M.\] Konverteringen mellom hundeår og menneskeår er egentlig mer komplisert enn formelen over tilsier. Likevel fungerer formelen som en tommelfingerregel som er god nok for dette prosjektet.
2.7.3 Første forsøk
La oss hoppe i det! Vi gjør et forsøk basert på forberedelsene over, så tar vi utfordringer når vi støter på dem. Et første forsøk ser slik ut:
alder_menneske = input('Hvor mange menneskeår er hunden din? ')
alder_hund = alder_menneske * 7
print(f'Hunden din er {alder_hund} hundeår gammel.')Dette ser jo rimelig rett fram ut. Hvis du kjører koden over så vil du kanskje bli overrasket når det ikke går som planlagt. Hvis jeg skriver inn at hunden min er 6 år, så får jeg ut setningen:
Hunden din er 6666666 hundeår gammel.
Dette er jo bare tull. Hva har skjedd?
2.7.4 Feilsøking
Det er åpenbart en logisk feil i programmet, men hvor er den? Kunsten å feilsøke problemer er en ferdighet du vil jobbe mye med. Et første steg for å finne feil er å skrive ut variablene. Stemmer de overens med det du forventer? Hvis svaret er nei, er vi på sporet av feilen vår:
alder_menneske = input('Hvor mange menneskeår er hunden din? ')
print('Variabelen alder_menneske:', alder_menneske)
alder_hund = alder_menneske * 7
print('Variabelen alder_hund:', alder_hund)
print(f'Hunden din er {alder_hund} hundeår gammel.')Kjører du koden på nytt og skriver inn tallet 6 som menneskeår ser du at variabelen alder_menneske holder verdien 6, mens variabelen alder_hund holder verdien 6666666. Det er altså noe som går galt i overgangen fra variabelen alder_menneske til alder_hund.
Et annet godt råd er å skrive ut datatypene til variablene du har:
alder_menneske = input('Hvor mange menneskeår er hunden din? ')
print('Variabelen alder_menneske har datatypen:', type(alder_menneske))
alder_hund = alder_menneske * 7
print('Variabelen alder_hund har datatypen:', type(alder_hund))
print(f'Hunden din er {alder_hund} hundeår gammel.')Kjører du koden over kan du se at vi har funnet feilen. Variabelen alder_menneske er en streng, og ikke et heltall som du kanskje tenkte. Funksjonen input() returnerer alltid brukerens informasjon som en streng. Dette fører til at alder_menneske * 7 er å gange en streng med tallet 7. Når du ganger en streng med et heltall så betyr dette i Python at du ønsker å repetere strengen så mange ganger som heltallet. Det er derfor vi får strengen 6666666 i variabelen alder_hund.
Nå som vi vet hva problemet er kan vi fikse det. Før du går videre må du huske å slette linjene med funksjonen print() du brukte for å finne feilen. Dette skal ikke være med i det endelige programmet.
2.7.5 Konvertering mellom datatyper
Vi trenger å konvertere verdien i variabelen alder_menneske til et heltall. Dette kan vi gjøre med funksjonen int(). La oss gjøre dette umiddelbart etter at vi tar inn informasjon fra brukeren med funksjonen input():
alder_menneske = int(input('Hvor mange menneskeår er hunden din? '))
alder_hund = alder_menneske * 7
print(f'Hunden din er {alder_hund} hundeår gammel.')Her omgjør vi funksjonen input() sin returverdi umiddelbart til et heltall med funksjonen int(). Hvis du kjører programmet nå, fungerer det som forventet. Forståelsen vår av datatyper gjør at vi kan finne og fikse opp i mange forskjellige feil raskt.
2.7.6 En liten forbedring
Programmet over gjør jobben sin når brukeren oppfører seg slik vi forventer. Men sett at brukeren skriver inn teksten syv som svar på spørsmålet 'Hvor mange menneskeår er hunden din? '. Tenkt på hva som vil skje før du kjører koden og skriver inn syv.
Som du kanskje kunne tenke deg til så krasjer hele programmet. Naturlig nok greier ikke funksjonen int() å forstå hvordan den skal konvertere teksten syv til et heltall. Vi mennesker forstår jo med en gang at brukeren ønsker å bruke tallet 7, men det er basert på vår forståelse av norsk språk.
En forbedring av programmet hadde vært at vi først kunne sjekke om informasjonen fra brukeren er et heltall. Hvis ikke kunne vi gitt brukeren en mer vennlig beskjed som 'Vennligst skriv inn bare sifre.'. Etter dette kunne programmet bli avsluttet slik at brukeren kan kjøre det på nytt med riktig informasjon. Dette er mye mer brukervennlig enn at brukeren må forholde seg til en Python-feilmelding angående funksjonen int().
For å gjøre denne forbedringen trenger vi at programmet vårt gjør forskjellige handlinger basert på hvilke verdier vi har. Dette er nøyaktig det vi skal lære i kapittel 3, så det er bare å glede seg! Etter å ha lest kapittel 3 kan du returnere til dette prosjektet og prøve å implementere forbedringen beskrevet her.
2.8 Oppgaver
Oppgave 1
Hva gjør følgende kode? Beskriv hvert steg!
neglelakk_pris = input('Hva koster neglelakken? ')
mascara_pris = input('Hva koster mascaraen? ')
total_pris = float(neglelakk_pris) + float(mascara_pris)
print(f'Den totale prisen for kjøpet ditt er {total_pris} kroner.')Oppgave 2
Du skriver et program som skal gi brukeren informasjon om været og temperaturen i dag.
Lag en variabel
værsom inneholder en beskrivelse av været som en streng. Lag en variabeltemperaturmed et heltall som inneholder antall grader i Celsius.Bruk symbolet
+og funksjonenstr()til å skrive ut en beskjed til brukeren om været og temperaturen.Bruk en formatert streng til å skrive ut den samme beskjeden som i forrige punkt.
Oppgave 3
Du kan bruke nøkkelordet in i Python til å sjekke om noe er en del av en streng. Kjører du koden under vil du få ut verdien True fordi 'epleslang' inneholder 'eple':
Sjekk om Python også anser 'eple' som en del av ordet 'Epleslang'. Merk at her har ordet 'Epleslang' stor forbokstav.
Oppgave 4
I seksjon 2.5 så vi et frampek til neste kapittel, der vi skal lære mer om betingede setninger – altså hvordan programmer kan ta ulike valg avhengig av situasjonen. Vi jobbet med følgende eksempel der vi beregnet frakt basert på kjøpesummen:
total_kjøpesum = 1250
if total_kjøpesum < 1000:
frakt = 0.1 * total_kjøpesum
else:
frakt = 0
print(f'Frakten for denne kjøpesummen er {frakt} kroner.')Skriv om koden slik at den i stedet håndterer følgende situasjon:
Hvis den totale kjøpesummen er over 1500 kroner, skal brukeren få 15 % rabatt på kjøpesummen.
Hvis kjøpesummen er 1500 kroner eller lavere, får brukeren ingen rabatt.
Programmet skal regne ut hvor stor rabatten er, og skrive dette ut til brukeren.
PS: Dersom du synes dette er litt vanskelig, har du min tillatelse til å sniklese litt i kapittel 3 før du løser oppgaven.
Oppgave 5 (Utfordrende)
I tillegg til titallsystemet og totallsystemet finnes det også et sekstentallsystem29. Heltall skrevet i sekstentallsystemet sies å være på heksadesimal form30. Her trenger vi flere symboler enn 0, 1, 2 … 8, og 9, og vi legger til symbolene a, b, c, d, e, og f. Så a representerer 10 i titallsystemet, mens f representerer 15 i titallsystemet.
I Python kan du skrive et heltall på heksadesimal form ved å legge på symbolene 0x foran tallet. Så vi kan skrive heltallet 26 i titallsystemet på heksadesimal form slik:
Opprett to variabler som holder verdiene
2cog3bpå heksadesimal form. Adder deretter verdiene og skriv dette ut til terminalen.Bruk funksjonen
type()til å sjekke at heltall skrevet på heksadesimal form har fortsatt heltall som datatype.I Python er det en innebygd funksjon som heter
hex()som konverterer heltall i titallsystemet til heksadesimal form. Bruk funksjonenhex()til å se hva som er den heksadesimale formen til heltallet42.
Heksadesimale tall blir brukt i alt fra fargekoder på nettsider til minneadresser i datamaskiner. De dukker opp iblant, og da er det greit å vite at vi enkelt kan håndtere dem i Python.