Kapittel 8 Pakker i Python
Velkommen til del 2 av boken! I dette kapittelet skal vi lære hvordan pakkesystemet i Python fungerer. Dette inkluderer standardbiblioteket, som er et sett med pakker som følger med når du installerer Python. Vi skal utforske noen av de viktigste pakkene i standardbiblioteket i seksjon 8.2. Det er også mulig å laste ned og bruke eksterne pakker som andre har skrevet, noe vi skal undersøke i seksjon 8.4. Vi får også en mulighet i seksjon 8.5 til å bli godt kjent med pip, som er en applikasjon i kommandolinjen for å blant annet installere eksterne pakker. Vi skal derimot ikke lære å skrive egne pakker i denne boken, siden dette er et mer avansert tema.
8.1 Introduksjon - Ikke finn opp hjulet på nytt!
Med alt vi har lært i del 1 av boken er det mange problemer vi kan løse. Sett at vi blir satt til å finne medianen til en liste med tall som representerer daglige iskremsalg for en kiosk. Listen blir et element større hver dag og ser slik ut:
Du husker kanskje at for en sortert liste så er medianen:
Den midterste verdien når lengden til listen er et oddetall.
Gjennomsnittet av de to midterste verdiene når lengden til listen er et partall.
Med litt betingede setninger og funksjonen sorted() er dette barnemat for deg:
iskremsalg = [198, 432, 278, 394, 89, 229, 112]
iskremsalg_sortert = sorted(iskremsalg)
antall_dager = len(iskremsalg_sortert)
median = 0
if antall_dager % 2 == 1:
median = iskremsalg_sortert[antall_dager // 2]
else:
nedre_midtpunkt = iskremsalg_sortert[(antall_dager // 2) - 1]
øvre_midtpunkt = iskremsalg_sortert[antall_dager // 2]
median = (nedre_midtpunkt + øvre_midtpunkt)/2
print(f'Medianen til listen er {median}')Utmerket! Her var det litt mer arbeid enn du kanskje tenkte med indeksene til listen. Vi må holde tungen rett i munnen for å hente medianen riktig når lengden til listen iskremsalg er jevn. Likevel fikk du det til og alle ble imponerte.
Dette var helt til noen spurte hvorfor du ikke bare brukte funksjonen median() fra pakken statistics? Denne funksjonen kan du nemlig bruke slik:
from statistics import median
iskremsalg = [198, 432, 278, 394, 89, 229, 112]
print(f'Medianen til listen er {median(iskremsalg)}')Dette var jo egentlig mye enklere. Det å skrive funksjonaliteten til median() fra bunn er en god treningsoppgave. Fordelene ved å bruke median() fra pakken statistics er derimot klare: Det er mange andre programmerere som har testet ut funksjonen median(). Sannsynligheten for at median() inneholder feil er dermed liten. Vår selvskrevne kode kan fort inneholde feil, spesielt hvis den ble skrevet sent på kvelden mens vi tenkte på andre ting. I tillegg blir det mindre kode for oss å vedlikeholde når vi bruker median().
Pakken statistics er med i standardbiblioteket til Python og er dermed allerede installert. Der finner du noen enkle statistikkfunksjoner som ofte er nyttige. Må du dermed lage mer avanserte statistiske funksjoner selv? Ikke nødvendigvis! Det er også en ekstern pakke som heter statsmodels som har mer avanserte statistikkfunksjoner du kan bruke.
Basert på diskusjonen over er det nyttig å kunne følgende:
Vite hva som er tilgjengelig i standardbiblioteket til Python.
Vite de forskjellige måtene å importere pakker i våre programmer.
Vite hvordan vi installerer eksterne pakker som andre har laget.
La oss starte med å få en oversikt over hvilken funksjonalitet du kan finne i Pythons standardbibliotek.
8.2 Standardbiblioteket
Standardbiblioteket i Python består blant annet av:
Innebygde datatyper som
boologint.Innebygde feil som
NameErrorogZeroDivisionError.Innebygde pakker i Python som pakken
statistics.
Kort sagt kan man si at standardbiblioteket er det som følger med når du installerer Python. Du kan finne en oversikt over alt som er i Pythons standardbibliotek her:
https://docs.python.org/3/library/index.html
En analogi du kan ha i hodet ditt er dette: Når du kjøper en ny laptop så er det en del innebygde programmer som følger med. Dette er analogt til standardbiblioteket i Python. Det er også mulig å laste ned andre programmer på den nye laptoppen din, som Spotify eller Visual Studio Code. Dette er analogt til eksterne pakker i Python.
Innebygde datatyper og feil vil vi lære mer om gjennom boken. La oss først se litt nærmere på innebygde funksjoner i Python. Deretter gir jeg noen eksempler på innebygde pakker i standardbiblioteket.
8.2.1 Innebygde funksjoner i Python
Som vi allerede har sett så kommer Python innebygd med omkring 70 funksjoner slik som print() og abs(). Det nøyaktige antallet avhenger av hvilken versjon av Python du har lastet ned. For de innebygde funksjonene er det ikke nødvendig å importere dem på toppen av filen. De innebygde funksjonene er alltid tilgjengelig.
En ting du må være obs på er at innebygde funksjoner kan overskrives. Det er fullt mulig å overskrive funksjonen print() slik som dette:
Kjører du koden over vil først 'Heisan' bli skrevet ut til terminalen ved å bruke funksjonen print() på vanlig måte. Deretter overskriver vi funksjonen print() til å holde heltallet 10. Når vi til slutt prøver å skrive ut 'Heisan' til terminalen igjen vil dette ikke gå. Siden print refererer til heltallet 10 får du feilmeldingen:
TypeError: ‘int’ object is not callable
Dette er en litt kryptisk feilmelding. Den forklarer at et heltall ikke er en funksjon, og dermed ikke kan bli kalt ved å bruke symbolene (). Poenget er at du aldri bør overskrive en innebygd funksjon i Python. Dette fører bare til feil som er vanskelige å finne ut av.
Å lære seg alle de innebygde funksjonene i Python er et maraton, og ikke en sprint. Du har så langt sett over 10 av dem fra før av i boken. Noen av funksjonene er knyttet til konsepter som du ennå ikke har lært. La meg lære deg tre nye innebygde funksjoner til før vi går videre slik at du er en kilometer nærmere målstreken på maratonet ditt.
I seksjon 2.2.1 så vi at binærtall ikke er en egen datatype i Python, men bare en alternativ måte å representere den samme informasjonen på som et heltall i titallssystemet. I standardbiblioteket til Python finner vi funksjonen bin() som gir ut en streng som representerer binærrepresentasjonen til et heltall i Python:
heltall = 10
heltall_bin = bin(10)
print(f'Tallet {heltall} har binærrepresentasjonen {heltall_bin}.')Kjører du koden over ser du at bin() gir ut binærrepresentasjonen til tallet med symbolene 0b først. Dette indikerer at det er en binærrepresentasjon i Python.
Du kan nå selv teste ut de innebygde funksjonene oct() og hex(). De gjør akkurat det samme som bin(), bare at de tilsvarer åttetallsystemet og sekstentallsystemet. På samme måte som bin() starter med 0b, vil resultatet fra oct() starte med 0o og resultatet fra hex() starte med 0x. Dette tydeliggjører hvilken representasjon som brukes. Verken bin(), oct(), eller hex() er funksjoner du vil bruke veldig ofte, men i noen applikasjoner er de veldig nyttige.
8.2.2 Innebygde pakker
I standardbiblioteket i Python er det mange nyttige pakker som følger med. Pakker må importeres slik vi så tidligere med statistics. I seksjon 8.4 skal vi se litt nærmere på pakken random siden den er veldig mye brukt.
Vi har ikke mulighet til å gå gjennom alle pakkene i standardbiblioteket her siden det er en god del av dem. Vi gir heller en overordnet beskrivelse av syv av pakkene i standardbiblioteket. På denne måten får du inntrykk av variasjonen som eksisterer i standardbiblioteket:
Pakken
datetimegjør det mulig å jobbe med datoer og tidspunkter. Uten pakkendatetimekan vi alltids lagre datoer og tidspunkter som strenger, som for eksempel'2017-07-08 23:59:59'. Dette er ikke så beleilig fordi vi ønsker å gjøre operasjoner på datoer og tidspunkter, som å legge til en time eller å se på tidspunkt 90 dager tilbake i tid. Ved å bruke pakkendatetimeer dette mye enklere.Pakken
stringgir oss mer funksjonalitet for å jobbe med strenger. Som et enkelt eksempel så finner du konstantenstring.ascii_uppercasesom rett og slett er strengen'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. På samme måte holderstring.ascii_lowercasestrengen'abcdefghijklmnopqrstuvwxyz'. Så om du trenger å sjekke om en bokstav er i det engelske alfabetet, så er dette en enkel sak medstring.ascii_uppercase,string.ascii_lowercase, og operatorenin. Du kunne selvfølgelig laget strengene med alle engelske bokstaver selv, men dette er deilig å slippe.Pakken
mathhar vi sett kort i seksjon 1.6. Den har mange vanlige funksjoner fra matematikk som er nyttig i vitenskapelige utregninger. Eksempler er de trigonometriske funksjonenemath.sin(),math.cos(), ogmath.tan(). Det er også nyttig å ha konstantenemath.piogmath.esom representerer pi og Eulers tall. Det meste av matematikken som læres på videregående kan representeres med å bruke pakkenmath.Pakken
timeitkan brukes til å sjekke hvor lang kode bruker å kjøre. Med hjelpefunksjonentimeit.timeit()kan du finne gjennomsnittlig hvor lang tid en bit med kode tar for å kjøre. Merk at resultatet er avhengig av ressursene du har tilgjengelig på din datamaskin. Dette er likevel veldig nyttig for å forstå hvilke funksjoner i Python som er rask å utføre, og hvilke som er sakte. Hvis du har skrevet et program som tar lang tid å utføre, så kan pakkentimeitvære til stor hjelp for å finne flaskehalsen som slakker ned programmet ditt.Pakken
tkinterbrukes til å lage desktop-applikasjoner med Python. Fremfor å lage programmer som må brukes i terminalen kantkinterlage desktop-applikasjoner som flere kan få nytte av. Det meste som blir laget direkte med pakkentkinterkan se litt gammeldags ut, men fungerer likevel veldig bra for internapplikasjoner i selskaper. Andre eksterne pakker kan utvide pakkentkinterfor å få applikasjonene til å se mer moderne ut.Pakken
threadinghjelper med å få Python til å veksle mellom forskjellige oppgaver. Dette kan være nyttig hvis en oppgave venter på ressurser fra et annet system. Under ventingen kan vi utføre andre oppgaver for å være effektiv. Her er det klassenthreading.Threadsom er startpunktet for å lage flere tråder68 som kan veksle mellom å utføre kode. Tråder er en del av et større område innenfor programmering som heter samtidighet69. Vi skal ikke dekke samtidighet i denne boken siden det er et mer avansert tema.Pakken
webbrowserer en enkel måte for Python og åpne nettsider på. Funksjonenewebbrowser.open_new()ogwebbrowser.open_new_tab()kan brukes til å åpne nettsidene du ønsker. Har du kanskje et vanlig sett med nettsider som du ønsker å åpne? Da kan du lage deg et lite program som åpner disse når du kjører filen. Pakkenwebbrowserhar ikke så mye mer enn dette til seg. Det er en enkel pakke som iblant er nyttig til mindre oppgaver.
Når du ser over de syv eksemplene ovenfor så kan du også trekke ut følgende. Noen pakker gir funksjonalitet som du ikke enkelt kunne lagt selv slik som tkinter og threading. Andre pakker som string har mye funksjonalitet som du egentlig kunne lagd selv. Likevel er string og andre enkle pakker nyttige for å unngå å finne opp hjulet på nytt igjen.
Nå er det på tide å se på forskjellige måter vi kan hente inn funksjonalitet fra pakker. Dette kommer til å gjelde for både pakkene i standardbiblioteket og den eksterne pakken som vi skal laste ned separert i seksjon 8.5.
8.3 Forskjellige måter å importere funksjonalitet
I Python er det flere måter å importere funksjonalitet med nøkkelordet import. La oss gå gjennom de forskjellige måtene og forklare når hver av dem er nyttig.
8.3.1 Importere hele pakker
Hvis du ønsker å importere en hel pakke kan du skrive import <pakkenavn>. Her er tre eksempler:
Nå har du tilgang til all funksjonaliteten i pakkene math, random, og threading. Det er også mulig å legge alle tre importene på samme linje slik:
Jeg synes selv at det er fint med separerte linjer for hver enkelt pakke. Det er også anbefalingen til PEP 8. Hvis du nå ønsker å bruke sinusfunksjonen sin() og konstanten \(\pi\) innad i pakken math så kan du bruke følgende notasjon:
Som du ser får du alt innen pakken math tilgjengelig. Med denne importmetoden må du derimot skrive math.<navn> for å bruke både funksjoner og konstanter i pakken math. Dette er nyttig hvis du skal bruke mange ulike funksjoner fra pakken.
8.3.2 Importere utvalgte objekter
Sett heller at du skal bruke et fåtall objekter fra en pakke, men du skal kanskje bruke dem veldig mange ganger. Det blir tungvint å skrive math.sin() hver gang dersom du skal bruke den 30 ganger. I slike situasjoner kan du bruke følgende notasjon:
Her bruker vi nøkkelordet from til å si at vi skal spesifikt hente funksjonen sin() fra pakken math. Med linjen from math import sin vil altså bare sinusfunksjonen bli tilgjengelig fra pakken math. Fordelen nå er at vi slipper å skrive math.sin() hver gang vi bruker sinusfunksjonen.
Ønsker du å hente flere spesifikke objekter fra samme pakke kan du skille dem med komma slik:
I motsetning til å importere flere pakker på samme linje så synes jeg at det er helt fint at flere objekter blir importert fra samme pakke på en linje. Hvorfor det? Her er det en sammenheng mellom objektene, nemlig at de kommer fra samme pakke. Da gir det for meg mening at de blir importert på samme linje. Det går likevel også helt fint å ha separerte linjer for forskjellige objekter i samme pakke slik:
Du kan blande mellom å importere en hel pakke og å importere utvalgte objekter fra en annen pakke. Sett at du skal bruke mange funksjoner fra pakken math, men bare funksjonen choice fra pakken random. Da ville importert dette slik:
8.3.3 Omdøping til pakker og objekter
For importering av både hele pakker og objekter fra pakker kan du gi dem nye navn umiddelbart. Dette kan du gjøre med nøkkelordet as. I koden under omdøper jeg funksjonen sin() fra pakken mathtil sinus():
Etter at jeg har omdøpt sin() til sinus() så må jeg referere til den som sinus(). Hadde jeg prøvd å referere til sin() ville jeg fått en NameError.
Du kan også omdøpe pakkenavn om du ønsker på samme måte:
Du lurer kanskje på om det er en god ide og omdøpe navn med nøkkelordet as? I de fleste tilfeller er ikke dette nødvendig, og det kan til og med være forstyrrende. Hvis du omdøper pakken math til mathematics, vil nok mange bli forvirret når du leser koden print(mathematics.sin(0.5)).
Når bør du egentlig omdøpe pakker og objekter som importeres? Her er to ganger dette er nyttig:
Noen ganger har pakker veldig lange navn, og de skal brukes ofte. Da er det ofte konvensjonen for å gi dem et kortere navn for enkelthets skyld. Pakkene
numpyogpandaser populære eksterne pakker for å jobbe med større datamengder. Her er den en konvensjon å skriveimport numpy as npogimport pandas as pd. Denne konvensjonen er såpass sterk at nærmest alle som brukernumpyogpandasbruker den. Har pakken en slik konvensjon kan dette brukes med god samvittighet.Noen ganger omdøper vi objekter slik at de ikke skal overskrive et eksisterende objekt. Husk at vi har en innebygd funksjon i Python som heter
max()som finner maksverdien i en liste. Det finnes også en funksjon i den eksterne pakkennumpysom hetermax()som er litt forskjellig. Dersom du hadde skrevetfrom numpy import maxså vil du overskrive den innebygde funksjonenmax(). Dette kan være kjipt hvis du trenger den innebygde funksjonenmax()i tillegg. I denne situasjonen ville jeg omdøpt funksjonenmax()i pakkennumpyved å skrivefrom numpy import max as numpy_max.
Utenom dette er det få grunner til å omdøpe verken pakker eller objekter. Bruk bare omdøping med nøkkelordet as hvis du har en god grunn til å gjøre det.
8.3.4 Importmåten som er farlig
Det er en siste importmåte vi ennå ikke har nevnt. Dette er å bruke symbolet * til å importere alle objektene fra en pakke:
På engelsk kalles dette for et wildcard import. Her har vi ikke et etablert norsk begrep. Dette er ikke så viktig siden du bør egentlig aldri gjøre dette!
Ved første øyekast virker jo * som en bra ide. Her importerer du alt fra pakken math, og du trenger heller ikke skrive math.sin() og math.pi for å bruke objektene. Hva kunne vært bedre enn dette?
Det store problemet er at du mister totalt kontroll over hvilke nye objekter som du har tilgjengelig. Dette gjør at det er fort gjort å overskrive eksisterende funksjoner og variabler. Det er også uklart for en leser hva du ønsker å bruke når du skriver from math import *. Hvis du bare bruker funksjonen sin() og variabelen pi så burde du heller skrivet from math import sin, pi. Dette er mye klarere!
Min sterke anbefaling er å ikke bruke notasjonen from <pakkenavn> import *. Det er likevel greit å vite om denne notasjonen ettersom noen få eksterne pakker bruker denne notasjonen i dokumentasjonen sin.
8.4 Et dypdykk i tilfeldige tall med pakken random
Vi skal i denne seksjonen se nærmere på pakken random som er i standardbiblioteket. Pakken random er mye brukt i forskjellige applikasjoner. Likevel er det egentlige målet i denne seksjonen ikke spesifikt pakken random. Målet er at du skal få erfaring med å lese dokumentasjonen til en pakke og trekke ut informasjon.
Du kan begynne med å lese dokumentasjonen ved å følge lenken:
https://docs.python.org/3/library/random
Hvis du ikke er vant med å lese dokumentasjon kan det som møter deg virke litt overveldende. Det er masse informasjon! En god fremgangsmåte er å bryte det ned i steg:
Introduksjonen til pakken: Les introduksjonen til pakken for å forstå overordnet hva den kan hjelpe deg med.
Utvalgte detaljer: Finn ett eksempel, en funksjonsdefinisjon, eller lignende som du prøver å forstå.
Implementer: Skriv kode som bruker funksjonaliteten du fant i steg 2 til å teste pakken ut selv.
Reflekter: Tenk over i hvilke situasjoner funksjonaliteten du brukte kan være nyttig.
Etter du har gått gjennom stegene over kan du gjenta stegene 2 - 4 flere ganger for å lære mer funksjonalitet. Det er alltid mulig å komme tilbake senere for å lære mer om en pakke. Det er også ofte ikke nødvendig å kunne alt som inngår i en pakke. Ofte er det å kunne 20 % av en pakke mer enn nok!
La oss sammen gå gjennom stegene over. Her håndholder jeg deg litt i begynnelsen, men litt sidesøking må du gjøre selv. Tenk på dette som en øvelse i å forstå informasjon på egen hånd.
8.4.1 Introduksjonen til pakken
Les gjennom introduksjonen til pakken. Hvis en pakke har en litt kryptisk introduksjon vil du ofte ikke forstå alt. Introduksjonen til pakken random begynner i skrivende stund med setningen "This module implements pseudo-random number generators for various distributions." Her støter du allerede på ord som kan være vanskelig å forstå hvis du ikke har jobbet med tilfeldige tall tidligere. Prøv å sidesøke begrepet pseudo-random number generators for å forstå dette bedre. Det meste av introduksjonen kan være vanskelig å forstå, men det er bra om du prøver likevel.
Bruk gjerne 10 minutter på å lese introduksjonen til en pakke for å forstå den best mulig. For noen pakker som er enkle er 10 minutter altfor mye. For noen pakker som er veldig omfattende er 10 minutter et absolutt minimum. Her kommer det an på hvor raskt du forstår formålet til pakken.
Jeg har bevisst valgt pakken random siden introduksjonen ikke er særlig brukervennlig. Introduksjoner snakker om distribusjoner, Mersenne Twister, klasser, og annet som du sannsynligvis vil synes er vanskelig å forstå. Prøv likevel å få en forståelse av hva pakken random gjør. Ikke vær redd for å sidesøke begreper eller rett og slett sidesøke hva random blir brukt til. Det finnes langt mer informasjon om Python på engelsk enn på norsk, så det er som regel lurt å sidesøke informasjon om Python på engelsk. Ikke gi opp! Tiden du bruker her er vel verdt frustrasjonen.
8.4.2 Utvalgte detaljer
Etter å ha lest over introduksjonen må vi hoppe ned til noen detaljer. Pakken har en eksempelseksjon som viser noen av de mest brukte funksjonene. Her vil du se funksjoner som random(), randrange(), og choice() bli brukt. La oss fokusere på et enkelt eksempel først, nemlig funksjonen choice(). Eksempelet i dokumentasjonen gir deg følgende informasjon:
`
Her virker det som funksjonen choice() henter ut et tilfeldig element fra en liste. Etter å ha sett på et eksempel kan det være greit å søke etter funksjonen choice() i dokumentasjonen for å se etter mer informasjon. Da vil du finne noe a la dette:
random.choice(seq) - Return a random element from the non-empty sequence seq. If seq is empty, raises IndexError.
Dette gjør det klarere at choice() funksjonen bare aksepterer et enkelt argument som er sekvensen du vil hente et tilfeldig element fra. Hva er en sekvens i Python? Eksempelet brukte en liste, nemlig ['win', 'lose', 'draw']. Er en liste og en sekvens det samme i Python? Hvis du sidesøker litt vil du finne ut at en sekvens er mer generelt enn en liste. Vi skal lære i neste kapittel om tupler, noe som også er sekvenser i Python. Uansett ser det ut som det er fint mulig å sette inn lister i funksjonen choice(), så dette er noe å jobbe videre med.
8.4.3 Implementer
Det er nå på tide å prøve ut funksjonen choice() selv. Du kan åpne en ny Python-fil og starte med å importere inn funksjonen choice() slik:
La oss lage en liste med personer og deretter bruke funksjonen choice() til å skrive ut en av dem:
from random import choice
personer = ['Eirik', 'David', 'Karoline']
favoritt_person = choice(personer)
print(f'Min favorittperson er {favoritt_person}!')Kjør koden over flere ganger. Du vil se at for hver gang vil en tilfeldig person bli valgt ut fra listen personer. Så funksjonen choice() fungerer slik vi tror. Det er alltid betryggende å teste selv for å være sikker på at vi forstår hvordan noe skal brukes.
La oss leke oss litt. Hva skjer hvis listen ikke har noen elementer? Vi prøver oss på samme koden, men med en tom liste med personer:
from random import choice
personer = []
favoritt_person = choice(personer)
print(f'Min favorittperson er {favoritt_person}!')Kjører du koden over vil du få en IndexError. Vi har tidligere sett denne feilen når vi prøvde å bruke en indeks som ikke eksisterer i en liste. Vi husker på at det stod If seq is empty, raises IndexError. i dokumentasjonen, så dette stemmer med hva som skulle skje. Når vi støter borti en IndexError når vi jobber med funksjonen choice(), så vet vi nå hva problemet mest sannsynlig er. Listen er da mest sannsynlig tom!
8.4.4 Reflekter
Nå forstår vi hva funksjonen choice() gjør. Et neste spørsmål er hvor dette kan være nyttig. Du kan tenke litt selv før du ser på mine to forslag under:
Hadde du laget et program som simulerer kortspillet poker så må spillere få utdelt tilfeldige kort. En kortstokk har jo 52 kort, så her kunne du brukt
choice()til å gi hver spiller noen tilfeldige kort. Du må bare passe på at hvert kort bare blir gitt en gang, slik at ikke alle spillerne sitter med fire ess på hånden.I et videospill er du kanskje i et område der en av tre utvalgte monstre plutselig kan dukke opp. Her kunne du brukt funksjonen
choice()til å tilfeldig velge hvilket av de tre monstrene som skulle dukke opp. I videospill blir tilfeldige valg brukt hele tiden for å skape en situasjon du ikke kan forutse.
Det å ha ett eller to eksempel på hvor du kan bruke choice() hjelper veldig. Du kan nå gå tilbake til dokumentasjonen og finne en ny funksjon som du har lyst å lære deg. Gjenta stegene 2 - 4 over for å lære deg mer! Et godt startpunkt er funksjonene random() og randint() i pakken random. Etter at du har sett litt mer på random pakken kan du gå videre til neste seksjon.
8.5 Eksterne pakker
Så langt har vi sett på pakker i standardbiblioteket til Python. Standardbiblioteket følger med Python og gir et godt grunnlag for å løse mange oppgaver. Likevel er det ufattelig mange flere pakker i Python enn dem som er i standardbiblioteket. Alle kan lage sin egen pakke i Python og publisere den slik at andre kan laste den ned. Dette kalles for eksterne pakker70.
I denne seksjonen skal vi lære å laste ned eksterne pakker med verktøyet pip. I tillegg skal vi lære om Python Package Index (PyPI) som er det vanligste stedet å finne eksterne pakker i Python. Ved å bruke eksterne pakker kan du bruke byggeklosser andre har laget til å lage kompliserte programmer.
8.5.1 Verktøyet pip
Når du laster ned Python så følger det med et verktøy som heter pip. For å sjekke om du har installert pip kan du åpne et terminalvindu og skrive inn kommandoen pip slik:

Hvis pip er installert så vil du som meg få opp en liste med mulige kommandoer og mer informasjon. Hvis du får en beskjed om at pip ikke er gjenkjent må du kjøre kommandoen python -m ensurepip --upgrade for å installere pip.
Som du ser av bildet over så er pip et kommandolinjeverktøy med en del forskjellige kommandoer. I denne seksjonen skal vi fokusere på kommandoen pip install. I neste seksjon skal vi se på noen av de andre kommandoene som pip tilbyr for å håndtere installasjon og administrasjon av pakker.
Som du sikkert har forstått så hjelper kommandoen pip install med å laste ned eksterne pakker. Kjør kommandoen pip install matplotlib i terminalen. Når du gjør dette vil det komme opp masse tekst som du i all hovedsak kan ignorere. Dette er tekst om detaljene til installasjonen av pakken matplotlib:

For å sjekke at pakken matplotlib har blitt installert kan du kjøre kommandoen pip show matplotlib. Her vil du få informasjon om matplotlib dersom den har blitt installert riktig.

Får du beskjed om at matplotlib ikke har blitt installert må du feilsøke problemet på nettet selv. Etter at matplotlib er installert kan du opprette en ny Python-fil og legge inn koden under:
import matplotlib.pyplot as plt
x_koordinater = [1, 2, 3, 4, 5]
y_koordinater = [1, 4, 9, 16, 25]
plt.plot(x_koordinater, y_koordinater)
plt.xlabel('X-akse')
plt.ylabel('Y-akse')
plt.title('En enkel visualisering')
plt.show()Kjører du koden over får du følgende pop-up vindu som visualiserer en enkel graf basert på informasjonen i x_koordinater og y_koordinater. Herlig!

Den eksterne pakken matplotlib gjør visualiseringer mulig i Python. Du kan lære mer om matplotlib ved å besøke hjemmesiden til pakken:
Det er flere andre eksterne pakker i Python som hjelper deg med visualiseringer. Noen av de mest populære er matplotlib, seaborn, plotly, og boketh.
En liten morsomhet om verktøyet pip: Du lurer kanskje på om pip er en forkortelse? Ifølge Ian Bicking som laget pip så betyr navnet Pip Installs Packages. Så den første bokstaven i pip står for pip… Om du ikke synes dette er særlig morsomt så er det i alle fall et eksempel på rekursjon71. Konseptet rekursjon skal vi se mer på i seksjon 13.4.
8.5.2 Python Package Index (PyPI)
Hvor henter verktøyet pip pakken matplotlib fra når vi installerer den? Med mindre noe annet er spesifisert så henter pip den nyeste versjonen av en pakke fra Python Package Index (PyPI).
Gå inn på nettsiden til PyPI og se deg litt rundt:
Som siden selv beskriver så er PyPI et oppbevaringsrom72 for eksterne pakker i Python. Søk etter pakken matplotlib i søkefeltet til PyPI og du vil finne noe som ligner dette:
Her kan du se hvordan du installerer pakken, nemlig med pip install matplotlib. Du kan også se:
Prosjektbeskrivelse - Dette fungerer ofte som en rask innføring i hva den eksterne pakken kan tilby.
Historikk - Informasjon om tidligere versjoner av pakken.
Lenker - Nyttige lenker til mer dokumentasjon enten på egne sider eller på sider som Github.
Bruk litt tid på å utforske nettsiden til PyPI. Hvis du finner noe du liker så kan du åpne en terminal og bruke pip install kommandoen til å laste ned den eksterne pakken. Etter dette er det bare å hoppe i gang med å utforske pakken.
8.6 Andre pip-kommandoer
I forrige seksjon så vi hvordan verktøyet pip kan brukes til å laste ned en ekstern pakke. Du kan bruke pip til å håndtere det meste knyttet til eksterne pakker i Python. La oss i denne korte seksjonen se på hva annet pip kan gjøre for oss.
For å se alle tilgjengelige kommandoer som pip gir deg tilgang til kan du skrive kommandoen pip --help i terminalen. Da vil du få ut følgende liste:
Som du kan se gir pip deg tilgang til i underkant av 20 kommandoer som kan være nyttige. Noen av dem er kun nyttig i mer avanserte situasjoner. La oss raskt gå gjennom de vanligste kommandoene til pip du vil bruke.Sett at du ønsker å slette en ekstern pakke. Da skriver du bare
pip uninstall <pakkenavn>så ordner pip dette for deg. Du kan prøve å skrivepip uninstall matplotlibfor å avinstallere pakkenmatplotlibsom vi installerte i forrige seksjon. Etter et “er du sikker?” spørsmål der du må svare medYvil pip fjernematplotlibfor deg.Ønsker du å oppdatere en ekstern pakke som du har en gammel versjon av? Da kan du skrive
pip install --upgrade <pakkenavn>. La oss si at det har kommet en ny versjon avmatplotlibsom du har hørt har en helt ny visualisering som du bare må teste. Da kan du bruke kommandoenpip install --upgrade matplotlibfor å oppgraderematplotlibtil nyeste versjon.La oss si at du vil installere en eldre versjon av en ekstern pakke. Du kan da skrive
pip install <pakkenavn>==<versjon>. Hvis du spesifikt har lyst på versjon 3.9.2 avmatplotlibså kan du derfor skrivepip install matplotlib==3.9.2. Som nybegynner vil du som regel installere den nyeste versjonen av en pakke. I større prosjekter er det derimot viktig å sørge for at pakker er kompatible med hverandre, samt at du ikke introduserer ny funksjonalitet som er dårlig testet. Derfor vil du da gjerne ha større kontroll over versjonene til pakkene du har installert.Lurer du på hvilke eksterne pakker du har lastet ned? Skriv bare
pip list, så får du opp en liste over alle de eksterne pakkene som du har lastet ned med versjonsnummeret til hver av pakkene. Dette er veldig nyttig for å få et raskt overblikk over hvilke eksterne pakker som er installert. Et tips er at du kan skrivepip list --outdatedfor å kun få opp de pakkene som ikke er oppdatert til nyeste versjon. Da sjekker pip etter nye versjoner for alle pakkene dine.
De resterende kommandoene i pip er litt mer avanserte. Bruk litt tid på å bli kjent med de vanligste kommandoene som nevnt ovenfor. Du kan heller slå opp de mer avanserte kommandoene når du trenger dem.
8.7 Prosjekt - QR-koder for en restaurantkjede
I dette prosjektet skal vi sammen laste ned en ekstern pakke som kan hjelpe oss med en veldig spesifikk oppgave. Målet her er ikke egentlig å lære all funksjonaliteten til den spesifikke pakken vi har valgt. Tenk heller at målet er å bedre forstå hvordan eksterne pakker kan hjelpe oss med å løse problemer.
8.7.1 Oppgaven
Du blir leid inn av den kjente norske restaurantkjeden Grommel for å løse følgende problem: Restaurantkjeden Grommel har over 40 restauranter i Norge der hver av dem har 30 bord for servering. Etter at noen andre har designet en elegant digital meny til Grommel ønsker de at besøkende kan bruke denne til å bestille mat. Det hadde gitt en god brukeropplevelse om hvert bord hadde en QR-kode som tok den besøkende til den digitale menyen, samtidig som bordnummeret ble automatisk lagt inn.
Siden restaurantkjeden Grommel har 40 restauranter med 30 bord hver så er det til sammen 1200 QR-koder som må skrives ut. Det å bruke et klikkebasert nettverktøy for å generere 1200 QR-koder vil ta temmelig lang tid. I tillegg vil det kanskje i fremtiden bli behov for å generere nye QR-koder hvis nettadressen til restaurantkjeden Grommel endrer seg, eller hvis nye lokasjoner åpner. Her kommer du inn i bildet som en fersk programmerer!
Hvordan kan vi bruke Python til å automatisk generere 1200 QR-koder for Grommel?
8.7.2 Forberedelse
Det første du kanskje tenker på er å lese litt om hvordan lage QR-koder. Dette kan vel ikke være så vanskelig, sant? Den følgende QR-koden tar deg til Wikipedia sin side om QR-koder:

Her lærer du at QR står for “Quick Response” og dette er en todimensjonal strekkode som ble oppfunnet i 1994. Det står likevel ikke noe om hvordan du lager en QR-kode. Grunnen er at det er en ganske komplisert algoritme som står bak QR-koder.
Fremfor å lære deg hvordan å lage QR-koder fra bånn hadde det vært fint om noen andre allerede har implementert dette i Python. Det er nettopp det mange allerede har gjort. Hvis du søker på “QR Code” på PyPI så vil du blant annet få opp den eksterne pakken qrcode. Her er lenken:
https://pypi.org/project/qrcode/
En rask titt på beskrivelsen til qrcode pakken forteller oss at dette er akkurat hva vi trenger!
8.7.3 Generering av en enkel QR-kode
For å installere pakken qrcode skriver du bare pip install qrcode[pil]. Hva betyr klammeparentesene i qrcode[pil]? Når du skal installere noen pakker har du gjerne følgende valg:
Installere pakken rett frem med
pip install <pakkenavn>.Installere pakken sammen med en annen pakke med
pip install <pakkenavn>[<annen pakke>].
I slike situasjoner vil den andre pakken gi ekstra funksjonalitet som hovedpakken kan benytte seg av. I vårt tilfelle vil pakken pil gi ekstra funksjonalitet i form av bildevisninger til pakken qrcode. Pakken pil er mye brukt for å håndtere bilder i Python.
Vi velger å installere hele sulamitten og bruker derfor pip install qrcode[pil]. Hvis du nå åpner en ny Python-fil kan du skrive følgende kode:
import qrcode
bilde = qrcode.make('https://no.wikipedia.org/wiki/QR-kode')
bilde.save('wikipedia.png')I koden over importerer vi først hele pakken qrcode. Etterpå bruker vi funksjonen make() til å lage en QR-kode av Wikipedia-siden vi besøkte tidligere. Da får vi et bilde-objekt som vi lagrer i variabelen bilde. Til slutt bruker vi den innebygde metoden .save() på dette bildeobjektet for å lagre bildet i filsystemet vårt.
Kjører du koden vil du få en fil som heter wikipedia.png i samme mappe som Python filen. Så programmet vårt fungerer til å lage en QR-kode.
8.7.4 Generering av mange QR-koder
Det er nå på tide å lage mange QR-koder. Sett at vi har en liste med nettlenker som hver QR-kode skal peke på:
nettlenker = [
'https://grommel.no/restaurant_14/bord_21',
'https://grommel.no/restaurant_14/bord_22',
'https://grommel.no/restaurant_14/bord_23',
'https://grommel.no/restaurant_14/bord_24',
]Her har vi for illustrasjonens skyld bare fire lenker fra restauranten med nummer 14 og bordene 21-24. Se likevel for deg at du har alle de 1200 lenkene her. Hvordan kan vi nå lage en QR-kode for hver av dem?
Her bruker vi ganske enkelt bare en for-løkke i Python. I tillegg trenger vi å lage et passende filnavn til bildene. Jeg tenker at bildene skal ha filnavn i formatet restaurant-14-bord-21.png. For å hente ut tallene som representerer restaurantnummeret og bordnummeret fra lenkene må vi skrive vår egen funksjon.
Prøv gjerne selv før du ser på min løsning under:
import qrcode
nettlenker = [
'https://grommel.no/restaurant_14/bord_21',
'https://grommel.no/restaurant_14/bord_22',
'https://grommel.no/restaurant_14/bord_23',
'https://grommel.no/restaurant_14/bord_24',
]
def hente_ut_bildenavn(lenke):
"""Funksjonen tar en nettlenke og henter ut bildenavn."""
restaurant_nummer = lenke.split('/')[-2][-2:]
bord_nummer = lenke.split('/')[-1][-2:]
return f'restaurant-{restaurant_nummer}-bord-{bord_nummer}.png'
for lenke in nettlenker:
bilde = qrcode.make(lenke)
bilde.save(hente_ut_bildenavn(lenke))Strengmetoden .split() deler en streng opp i biter som er separert av argumentet i .split(). Så når vi skriver lenke.split('/') vil vi få listen
['https:', '', 'grommel.no', 'restaurant_14', 'bord_21']
for den første nettlenken. Deretter bruker vi negativ indeksering til å hente ut informasjonen om restaurant og bord. Til slutt bruker vi slicing med negativ indeksering for å hente ut tallene 14 og 21. Her gjelder det å holde tungen rett i munnen!
Kjører du koden over så vil du generere fire QR-koder i samme mappe som Python-filen. Navnet på den første filen vil være restaurant-14-bord-21.png, osv. Du kan nå generere alle QR-kodene ved å legge flere lenker inn i listen nettlenker. I praksis ville du nok ikke hatt en enorm liste til dette, men heller lest nettlenkene fra en fil. Dette skal vi se nærmere på i kapittel 10.
Merk deg hvor mye tid vi sparte på å bruke en ekstern pakke. Likevel blir det helt feil å si at vi ikke trengte å kunne skrive kode i Python. Vi måtte både skrive en for-løkke, samt skrive vår egen funksjon for å lage gode bildenavn. Som regel er vi nødt til å skrive litt egen logikk selv om vi bruker eksterne pakker. Hvis du både kan skrive kode i Python og har kjennskap til en del pakker kommer du langt. Restaurantkjeden Grommel ville nok vært fornøyd med QR-kodene du har laget!
8.8 Oppgaver
Oppgave 1
Hva gjør følgende kode? Beskriv hvert steg!
from datetime import datetime, timedelta
nå = datetime.now()
print('Akkurat nå er det:', nå)
ukedager = [
'mandag', 'tirsdag', 'onsdag',
'torsdag', 'fredag', 'lørdag',
'søndag'
]
print('I dag er det:', ukedager[nå.weekday()])
om_100_dager = nå + timedelta(days=100)
print('Datoen om 100 dager er:', om_100_dager.date())
jul = datetime(nå.year, 12, 24)
if nå > jul:
jul = datetime(nå.year + 1, 12, 24)
dager_til_jul = (jul - nå).days
print('Det er', dager_til_jul, 'dager igjen til jul!')Slå opp funksjonalitet i standardbiblioteket datetime hvis det er deler av koden du er usikker på.
Oppgave 2
To innebygde funksjoner som ofte kommer til nytte er any() og all(). Dette er innebygde funksjoner du kan bruke på en liste slik:
Koden
any(liste)returnererTrueom noen av elementene i listenlisteevalueres til å være sann. Ellers returneresFalse.Koden
all(liste)returnererTruedersom alle elementene i listenlisteevalueres til å være sann. Ellers returneresFalse.
Bruk funksjonen all() til å forenkle følgende kode:
fornavn = ['Eirik', 'Kari', '', 'Børre']
inneholder_tom_streng = False
for navn in fornavn:
if not navn:
inneholder_tom_streng = True
if inneholder_tom_streng:
print('Listen inneholder en tom streng.')Kan du komme på et eksempel der funksjonen any() kan brukes til å forenkle kode?
Oppgave 3
Du har arvet en Python-fil skrevet av en kollega som har rotet litt med importene sine. Nå er det din jobb å rydde opp slik at koden blir mer lesbar. Slik ser koden ut:
from math import *
import datetime
import random as r
from os import path
import statistics, calendar, math, sys
from collections import defaultdict
from collections import Counter
from collections import deque
import datetime
print(sqrt(49))
print(datetime.datetime.now())
print(r.randint(1, 10))
print(path.exists('rotete_kode.py'))
print(statistics.mean([1, 2, 3]))
print(calendar.isleap(2024))Bruk prinsippene du har lært i kapittelet sammen med godt folkevett til å forenkle importene i koden.
Oppgave 4
Under ser du et utklipp som viser oversikten over alle innebygde funksjoner i Python:

Du finner også oversikten på:
https://docs.python.org/3/library/functions.html
Finn ut hvordan divmod() fungerer ved å lese dokumentasjonen. Prøv å skrive minst to eksempler i Python som viser hvordan funksjonen kan brukes.
Oppgave 5 (Utfordrende)
Python har ikke bare tørre fakta og tall. Det finnes også eksterne pakker som rett og slett gjør ting litt morsommere. En slik pakke er emoji, som lar deg skrive ut emojis i terminalen din! 🎉🐍
Kjør først kommandoen pip install emoji for å installere den eksterne pakken emoji. Du kan finne informasjon om den eksterne pakken emoji på PyPI:
https://pypi.org/project/emoji/
Bruk pakken emoji til å gjøre meldinger litt gøyere. Du kan enten legge emojis til et program du har skrevet tidligere, eller lage noe helt nytt.