- Kursmaterial
- Planering
- Arbete
- Kunskapsdokument
- Andra kurser
- Om Kursolle
5. Moment05 - Funktioner och datalagring
Funktioner och datalagring är bra redskap för att skapa en bättre och tydligare struktur i applikationer. Funktioner används för att samla exempelvis beräkningar som ska göras flera gånger i koden under ett funktionsnamn. Det är också möjligt att skapa separata .py filer där alla funktioner som används i en kod samlas.
I avsnittet om datalagring kommer vi titta lite på hur vi med hjälp av Python kan läsa, manipulera och skriva data till textfiler (.txt). Detta kommer vara ett ytterligare steg i vår resa att arbeta med applikationer som består av flera olika filer som påverkar varandra.
Som avslutning på detta moment så skall du få bygga en större applikation då du skall bygga en liten bankapplikation.
Info
Till detta moment finns en sida med lösningsförslag och videoklipp.
5.1 Funktioner
Genomgång [klicka för att visa]
Vi har tidigare tittat på några inbyggda funktioner och det finns väldigt många sådana men vi kan också skapa egna funktioner.
En funktion är en del kod som utför någon beräkning, utskrift eller något liknande som vi behöver i vår kod. En funktion är perfekt att använda till saker som skall göras flera gånger, behöver vi ändra något så ändras det bara på ett ställe och slår igenom i hela applikationen. I vissa programmeringsspråk pratar vi om metoder men det är samma sak som en funktion.
Grundtanken är att en funktion skall göra en sak, behöver vi utföra två beräkningar är det ofta bättre att ha en funktion till varje beräkning.
Varför vill vi ha två funktioner? Tänk dig att vi skall skapa funktioner för att beräkna area och omkrets på en cirkel. I detta exempel kan vi skapa en gemensam funktion som räknar ut båda sakerna samtidigt och skriver ut resultatet, men sedan vill vi bara beräkna arean i en annan uppgift och har vi då en sammansatt funktion så kan vi inte använda den utan måste skapa en ny. Av denna anledning så är det enklare att skapa två funktioner som vi anropar.
5.1.1 Funktionsanrop
Genomgång [klicka för att visa]
Låt oss återvända till grunderna och den klassiska applikationen Hello World och bygga om applikationen så att det innehåller en egen funktion, hello_world().
Kodexempel: Hello World-funktionen
def hello_world(): print('Hello, World!')
För att definiera en ny funktion börjar vi med att skriva
, följt av funktionens namn. Lägg märke till att likt loopar måste vi ha ett i slutet på raden med funktionsnamnet och allt som ska tillhöra funktionen måste indenteras.Skulle vi köra applikationen som det ser ut nu kommer ingenting att hända. Detta beror på att funktionen endast är definierad, men aldrig anropas.
Kodexempel: Hello World-funktionen med funktionsanrop
def hello_world(): print('Hello, World!') hello_world()
Utskrift
Hello, World!
Var noga med att inte anropa funktioner innan de definierats, nedanstående exempel producerar en error:
Kodexempel: Hello World-funktionen med funktionsanrop
hello_world() def hello_world(): print('Hello, World!')
Error
NameError: name 'hello_world' is not defined
Uppgift: m05u01
Skapa en funktion som skriver ditt namn, din klass och din skola. Anropa sedan funktionen.
5.1.2 Inparametrar
Genomgång [klicka för att visa]
För att kunna skapa funktioner som kan göra lite mer än att bara skriva ut samma sak varje gång så kommer vi behöva skicka in något i funktionen som den kan jobba med. Det som skickas in till en funktion kallas inparameter. Inparametrar kan vara av vilken datatyp som helst, så det är viktigt att du kommenterar vad dina funktioner gör och vilken datatyp funktionen förväntar sig.
Ska vi i en applikation utföra addition av två tal och skriva ut svaret upprepade gånger kan det vara fördelaktigt att skapa en funktion som beräknar summan.
Kodexempel: En enkel summafunktion
def summa(a, b): c = a + b print('{} + {} = {}'.format(a, b, c)) summa(1, 3) summa(1.5, 3.14) a = 2 b = -5 summa(a, b) summa(4567, 8901) x = 2 y = 13 summa(x, y)
Utskrift
1 + 3 = 4 1.5 + 3.14 = 4.640000000000001 2 + -5 = -3 4567 + 8901 = 13468 2 + 13 = 15
5.1.2.1 lokala vs globala variabler
I exemplet ovan så är variablerna a och b inne i funktionen
lokala. Med detta menas att a och b endast existerar inuti funktionen och om vi har skapat variablerna a och b utanför funktionen är dessa globala variabler. De lokala och globala variablerna påverkas inte av varandra så länge vi inte gör ett aktivt val.Detta kan vara lurigt att skapa två olika variabler som har samma namn om de skall representera olika saker. I exemplet ovan hade det varit bättre att döpt om inparametrarna till något annat för att slippa risken att det blir något fel. Men ibland kan det finnas läge då vi vill kunna påverka en global variabels värde inne i en funktion. Tänk exempelvis att vi har byggt en applikation för att hålla koll på ett bankkontos saldo. I denna applikation så har vi en global variabel
som vi vill kunna uppdatera inne i funktionerna och . Då kan det vara smidigt att både kunna komma åt saldot och även uppdatera det inifrån funktionerna.Här kommer ett exempel på hur det fungerar med inparametrar respektive globala variabler.
Kodexempel: Global variabel vs inparameter
def change_tal_parameter(tal): tal += 1 print(f"Talets värde inne i funktionen: {tal}") def change_tal_global(): global tal tal += 1 print(f"Talets värde inne i funktionen: {tal}") tal = int(input("Mata in ett heltal: ")) print(f"Det inmatade talet är: {tal}") change_tal_parameter(tal) print(f"Talets värde efter funktionen med inparameter: {tal}") change_tal_global() print(f"Talets värde efter funktionen som deklarerar variabeln som global: {tal}")
Utskrift
Mata in ett heltal: 5 Det inmatade talet är: 5 Talets värde inne i funktionen: 6 Talets värde efter funktionen med inparameter: 5 Talets värde inne i funktionen: 6 Talets värde efter funktionen som deklarerar variabeln som global: 6
Att använda globala variabler har sina fördelar men det är viktigt att du förstår vad det innebär. Det är bra i vissa lägen men inte i alla.
Uppgift: m05u02
I inlämningsuppgiften till moment02 så byggde du en applikation där användaren fick ange radien till en cirkel och sedan så beräknade du area och omkrets som sedan skrevs ut. Bygg nu om denna så att utskrifter för area och omkrets utförs i två olika funktioner.
5.1.3 Returvärde
Genomgång [klicka för att visa]
I funktionerna
och är det främsta syftet kanske inte att skriva ut någon snygg formaterad text, utan snarare att utföra någon beräkning.Vill vi bara få ut det beräknade värdet i en funktion kan vi använda
.Beräkna area av rektangel
a = 3 b = 4 c = 5 d = 6 def rektangel_area(sida1, sida2): area = sida1 * sida2 return area print(f"En rektangel med sidorna {a} och {b} har arean {rektangel_area(a, b)}") print(f"och en rektangel med sidorna {c} och {d} har arean {rektangel_area(c, d)}") rektanglar_summa = rektangel_area(a, b) + rektangel_area(c, d) print(f"Summan av rektanglarnas areor är {rektanglar_summa}")
Utskrift
En rektangel med sidorna 3 och 4 har arean 12 och en rektangel med sidorna 5 och 6 har arean 30 Summan av rektanglarnas areor är 42
I exemplet ovan så kan du se hur jag låter funktionsanropet,
fungera som en variabel vid utskrift på rad 10. funktionsanropet kommer i detta fallet behandas som svaret från funktionen vilket är ett tal.Uppgift: m05u03
Arbeta om funktionerna i uppgift m05u02 så att det endast är värdet på cirkelns area och omkrets som returneras från funktionerna.
Uppgift: m05e01 (extra) (omdöpt från m05u04)
Arbeta om inlämningsuppgift m03 (den svåraste nivån du gjorde) och skapa funktioner som utför de olika skatteberäkningarna i uppgiften.
5.1.4 Funktioner i separat fil
Genomgång [klicka för att visa]
Vi ska nu ta första klivet från att endast jobba i en fil per applikation och skapa en separat fil, functions.py, där vi lägger in våra funktioner.
Detta är ett smidigt sätt att snygga till våra filer och förhoppningsvis öka läsbarheten.
Kodexempel: functions.py
def hello_world(): print('Hallå, världen!') def summa(tal1, tal2): return tal1 + tal2 def division(täljare, nämnare): return täljare / nämnare def rektangel_area(sida1, sida2): if sida1 == sida2: area = sida1**2 return area else: area = sida1 * sida2 return area
Det viktiga att tänka på är att alla filer som tillhör samma projekt eller applikation läggs i samma mapp. I det här fallet ligger filen functions.py i samma mapp som test.py.
Här följer tre olika sätt att använda functions.py från test.py.
Kodexempel: Variant 1 av test.py
# För att komma åt funktioner från functions.py måste vi # skriva functions.funktionens_namn() import functions functions.hello_world()
Kodexempel: Variant 2 av test.py
# Importerar alla funktioner från functions.py och vi # behöver inte använda functions.funktionens_namn() from functions import * hello_world()
Kodexempel: Variant 3 av test.py
# Importerar endast funktionen hello_world och ger den # Importerar funktionerna hello_world och rektangel_area # och ger funktionerna nya anropsnamn from functions import hello_world as hello from functions import rektangel_area as rekt hello() print(rekt(5, 3))
Uppgift: m05u05
Flytta ut alla dina funktioner från m05u03 i en separat fil. Importera sedan funktionerna på valfritt sätt.
Tidigare filmad genomgång av funktioner [klicka för att visa]
Här är en film som jag tidigare har spelat in som går igenom alla delar så här långt i momentet. Fördelen med denna film är att jag i en enda film går igenom de olika delarna som du som elev behöver kunna.
En liten varning för ljudet är på sin plats, det är lite ojämnt och det låter som att jag ibland använder en kofot för att skriva på tangentbordet.
5.1.5 Dokumentation av funktioner
Genomgång [klicka för att visa]
När vi skriver egna funktioner börjar dokumentationen bli allt viktigare, särskilt när du senare i kursen kommer arbeta på projekt som sträcker sig över längre tid.
Det kan vara bra att skaffa sig en god vana i att dokumentera sina funktioner tydligt men kortfattat.
5.1.5.1 Enkelt sätt att dokumentera funktioner
Innan vi går in på pythonspecifika sätt att kommentera och dokumentera funktioner så kan det vara bra att se på ett allmänt exempel. Detta sätt att kommentera fungerar alltid.
Kodexempel: Enkel dokumentation av funktioner
# area_kvadrat # funktionen beräknar arean av en kvadrat # inparameter: sida, kvadratens sida # returvärde: kvadratens area def area_kvadrat(sida): area = sida ** 2 return area
Det viktigaste är att ni kommenterar koden och funktionerna, sedan vilken teknik ni använder er av har underordnad betydelse. Tänk på att du snart kommer bygga större projekt med många funktioner som ligger i en egen fil, då är det viktigt att kommentera/dokumentera dessa funktioner på ett bra sätt.
I en enklare och kortare funktion så räcker det ofta med denna typ av dokumentation. Givetvis är det alltid bra att komplettera med kommentarer för koden också.
5.1.5.2 Pythons egen struktur
I python finns tydliga rekommendationer kring hur saker dokumenteras, bland annat funktioner. Detta är inget unikt med Python utan de flesta programmeringsspråk har sin standard. Ofta vill man använda en standard för att sedan kunna generera dokumentationer av dessa kommentarer. Nedan följer några exempel samt en checklista att följa när du dokumenterar dina funktioner.
Kodexempel: Funktionsdokumentation flera rader
def division(täljare, nämnare): """Beräknar kvoten mellan två tal. Nämnaren får ej vara 0. """ return täljare / nämnare
I exemplet ser vi att på första raden ges en kort förklaring på vad funktionen gör. Nästa rad lämnas tom för att markera att sammanfattningen är slut och på den tredje raden står det en kort information om att nämnaren ej får anges som 0.
Märk att i sammanfattningen ska inte funktionens namn anges. Sammanfattningen ska dessutom inte vara för teknisk.
Kodexempel: Fler dokumentationsexempel
def hello_world(): """Skriver ut hallå världen.""" print('Hallå, världen!') def summa(tal1, tal2): """Beräknar summan av två tal.""" return tal1 + tal2 def rektangel_area(sida1, sida2): """Beräknar arean av en rektangel.""" if sida1 == sida2: area = sida1**2 return area else: area = sida1 * sida2 return area
Checklista funktionsdokumentation
- Bygg upp dokumentationen i en sträng med 3 dubbelfnuttar.
- Den första raden är en sammanfattning / kort förklaring av funktionem, inled med stor bokstav, avsluta med punkt.
- Den andra raden ska vara tom.
- Från rad tre kan du börja skriva detaljer om hur funktionen fungerar, dess begränsningar osv.
Mer läsning för den intresserade.
5.1.5.3 Pythons dokumentation i PyCharm
Om du använder pycharm och börjar skriva tre citationstecken och trycker på enter
så kommer du få fram hela funktionens struktur inom kommentaren. Då kan det se ut på följande sätt;
Kodexempel: pycharms dokumentationshjälp
def area_kvadrat(sida): """ Funktion som beräknar arean på en kvadrat. :param sida: kvadratens sida :return: kvadratens area """ area = sida ** 2 return area
Där
står för den inparameter som finns till funktionen och står för vad som skall returneras från funktionen.Uppgift: m05u06
Fortsätt på m05u05 och kommentera dina funktioner enligt någon av exemplen för att kommentera funktioner.
Uppgift: m05u07 - julvarianten
Filen m05u07.py får bara innehålla följande:
Filen m05u07.py
from m05u07_funktioner import * julgran(10) tomten()
Din uppgift är att skapa och arbeta med filen m05u07_funktioner.py så att m05u07.py producerar följande utskrift:
Kodexempel: Utskriften
* ** *** **** ***** ****** ******* ******** ********* ********** Ho, Ho, Ho! Jullovet är nära
Hjälp [klicka för att visa]
Om man vill skriva ut flera tecken, eller en sträng, som är lika så går det också att skriva ut på följande sätt;
Kodexempel: multiplicerad utskrift
print("text"*3) print("X"*5)
Utskrift
texttexttext XXXXX
Läge att träna på aktivitetsdiagram och pseudokod om du inte har lyckats med detta tidigare?
Tillägg (svårare) [klicka för att visa]
I grunduppgiften är granen du producerar inte så snygg, den är ju bara halv och saknar en fot.
Redigera funktionen julgran så en ordentlig julgran ritas ut.
Utskrift
* *** ***** ******* ********* *********** ************* *************** ***************** ******************* *** *** Ho, Ho, Ho! Jullovet är nära
Även denna julgran går ju att bygga ut, så här kan den se ut när jag har slumpat fram röda julgranskulor och en gul stjärna i toppen på min gröna gran.
Uppgift: m05u07 - den andra varianten
Filen m05u07.py får bara innehålla följande:
Filen m05u07.py
from m05u07_funktioner import * area_rektangel(4, 6)
Din uppgift är att skapa och arbeta med filen m05u07_funktioner.py så att m05u07.py producerar följande utskrift:
Kodexempel: Utskriften
###### ###### ###### ###### Rektangelns area är 24.
Hjälp [klicka för att visa]
Om man vill skriva ut flera tecken, eller en sträng, som är lika så går det också att skriva ut på följande sätt;
Kodexempel: multiplicerad utskrift
print("text"*3) print("X"*5)
Utskrift
texttexttext XXXXX
Läge att träna på aktivitetsdiagram och pseudokod om du inte har lyckats med detta tidigare?
Tillägg (svårare) [klicka för att visa]
Det går att sätta defaultvärden på en inparameter så att du kan använda en funktion på flera olika sätt. Här kommer ett exempel.
Kodexempel: defaultvärde för en inparameter
def min_funktion(param1, param2="standardvärde"): print(f"Param1: {param1}") print(f"Param2: {param2}") # Anrop av funktionen med båda parametrarna min_funktion("värde1", "värde2") # Anrop av funktionen med bara en parameter min_funktion("värde1")
Utskrift
Param1: värde1 Param2: värde2 Param1: värde1 Param2: standardvärde
Bygg nu om filen m05u07_funktioner.py till m05u07b_funktioner.py så att anropen ger rätt utskrifter.
Filen m05u07b.py
from m05u07b_funktioner import * area_rektangel(4) area_rektangel(3,6) area_rektangel(2,3,"*")
Utskrift
#### #### #### #### Kvadratens area är 16. ###### ###### ###### Rektangelns area är 18. *** *** Rektangelns area är 6.
5.2 Datalagring
Datalagring är bra att ha koll på av flera olika anledningar. Utvecklare kan ibland vilja skicka ut felmeddelande och variabelvärden i en loggfil. En fysiker som genomfört ett experiment för att bestämma hur mycket vi påverkas av gravitationskraften från jorden här i Alingsås har samlat in några hundra olika datapunkter i ett textdokument som nu ska analyseras. Då är det bra att ha koll på hur vi kan underlätta filhantering och arbeta med datalagring i Python.
En fil på en dator är en sammanhängande mängd data som lagras under ett specifikt namn. Vi kan välja att skapa en fil med massa olika ändelser men eftersom vi i början väljer att skapa textfiler så är det lämpligt att ge våra filer ändelsen ".txt".
I denna kurs kommer vi fokusera på enkla filer för datalagring. Men inom datalagring så används en mängd olika tekniker för att lagra och transportera data, du kanske har hört talas om XML, JSON, databaser eller olika API:er. Mer om detta kommer i senare kurser.
När vi läser in data från textfiler är det viktigt att komma ihåg att all data som läses in är av datatypen string om vi inte aktivt gör något åt det. Till exempel genom att utnyttja den enkla konverteringen
för att konvertera till heltal.5.2.1 Läsa från fil
Genomgång [klicka för att visa]
Filen vi kommer arbeta med heter datatyper.txt och är en enkel textfil som innehåller fyra rader text. Den här filen kommer vi arbeta med under hela avsnitt 5.2.1
datatyper.txt
string | "@", "Johan", 'Sebastian' | Alla texter och tecken. Markeras med dubbelfnutt(") eller enkelfnutt(') int | 1, 7, -9 | Står för integer och lagrar heltal float | 0.0, 4.5, -9.7 | Flyttal, lagrar decimaltal, notera decimalpunkt enligt engelskspråkig standard boolean | True, False | Boolesk variabel, lagrar sant eller falskt, ofta ett resultat av ett uttryck som (1 < 2)
Innan vi tittar på olika sätt att läsa och skriva ut data från datatyper.txt måste vi öppna filen i vår .py fil med namnet test.py. I exemplet kan du se två alternativ.
Kodexempel: metod 1
# Öppnar filen med open('filnamnet') och sparar i en variabel f = open('datatyper.txt') # Här skriver vi kod som läser data från textfilen datatyper.txt # Stänger filen med .close(). Glömmer du det här riskerar du stöta på allehanda problem med att redigera textfilen. f.close()
Kodexempel: metod 2
# Med kommandot with håller python filen öppen filen tills det att programmet avslutas # eller vi hamnar utanför with. # Använder vi with kan vi alltid vara säkra på att filen stängs korrekt i slutet av programmet, # även om programmet avslutas oväntat på grund av en error. with open('datatyper.txt') as f: # Här skriver vi kod som läser data från textfilen datatyper.txt. # Glöm inte att indentera! # Skriver vi kod här har python stängt filen (utanför with).
När vi arbetar med filer i Python är det rekommenderat att använda metod 2, främst för att där säkerställs att filen stängs korrekt oavsett vad som händer i applikationen.
5.2.1.1 Dumpad utskrift
Det enklaste sättet att skriva ut data från en fil är helt enkelt att skriva ut allt som filen innehåller på en gång.
Kodexempel: Dumpad utskrift
with open('datatyper.txt') as f: print(f.read())
Utskrift
string | "@", "Johan", 'Sebastian' | Alla texter och tecken. Markeras med dubbelfnutt(") eller enkelfnutt(') int | 1, 7, -9 | Står för integer och lagrar heltal float | 0.0, 4.5, -9.7 | Flyttal, lagrar decimaltal, notera decimalpunkt enligt engelskspråkig standard boolean | True, False | Boolesk variabel, lagrar sant eller falskt, ofta ett resultat av ett uttryck som (1 < 2)
5.2.1.2 Läsa radvis från fil
Vill vi ha lite mer kontroll kan vi med hjälp av for-loopar arbeta med textfilen rad för rad.
Kodexempel: Arbeta med textfilen rad för rad
with open ('datatyper.txt') as f: for rad in f: print (rad, end='')
Utskrift
string | "@", "Johan", 'Sebastian' | Alla texter och tecken. Markeras med dubbelfnutt(") eller enkelfnutt(') int | 1, 7, -9 | Står för integer och lagrar heltal float | 0.0, 4.5, -9.7 | Flyttal, lagrar decimaltal, notera decimalpunkt enligt engelskspråkig standard boolean | True, False | Boolesk variabel, lagrar sant eller falskt, ofta ett resultat av ett uttryck som (1 < 2)
Här står det
i utskriften och det beror på att print()-funktionen i normala fall ger en radbrytning. Nu finns det dessutom en radbrytning i filen vilket gör att det skulle bli dubbla radbrytningar.5.2.1.3 Lagra textfil i en lista
Det är ofta relevant att inte bara läsa från en textfil, utan att lagra i en lista för att senare i programmet kunna arbeta med listan utan att påverka textfilen. Sparar vi data i en lista kan vi dessutom utnyttja listfunktioner som sort() för att snabbt sortera värden i storleksordning.
Kodexempel: Lagra i en lista
datatyp = [] with open('datatyper.txt') as f: for rad in f: datatyp.append(rad) # Här har textfilen stängts men eftersom raderna sparats i en lista kan vi komma åt innehållet. for text in datatyp: print(text)
Utskrift
string | "@", "Johan", 'Sebastian' | Alla texter och tecken. Markeras med dubbelfnutt(") eller enkelfnutt(') int | 1, 7, -9 | Står för integer och lagrar heltal float | 0.0, 4.5, -9.7 | Flyttal, lagrar decimaltal, notera decimalpunkt enligt engelskspråkig standard boolean | True, False | Boolesk variabel, lagrar sant eller falskt, ofta ett resultat av ett uttryck som (1 < 2)
Här ser du utskriften med dubbla
radbrytningar, dels i p -funktionen men också från filens egna radbrytningar. Jämför med utskriften i exemplet ovan.
5.2.1.4 Lagra textfil i flera listor
I datatyper.txt finns det tydliga tecken på att varje rad innehåller tre olika delar: namnet på en datatyp följt av exempel som följs av en förklaring. Varje del avgränsas med en så kallad delimiter, en avdelare. I datatyper.txt
har | använts som avdelare.
Det finns ett värde i att i en och samma textfil använda samma avdelare genom hela filen. Då kan vi nämligen smidigt dela upp de olika delarna i olika listor. Enklaste sättet att dela upp raderna i en textfil efter deras avdelare är att använda
som delar upp strängen i en lista.Kodexempel: Lagra i flera listor
datatyp = [] ex_datatyp = [] info_datatyp = [] with open('datatyper.txt') as f: for rad in f: rad = rad.split('|') # Tar bort eventuella inledande "whitespaces" med .lstrip() # .rstript() tar bort i slutet och .strip() tar bort både i början och slutet. datatyp.append(rad[0].lstrip()) ex_datatyp.append(rad[1].lstrip()) info_datatyp.append(rad[2].lstrip()) for i, t in enumerate(datatyp): # Vänsterformaterar text och skapar lagom mellanrum med {:<15} print('{:<15}{}\n{:<15}{}\n{:<15}{}'.format('Datatyp:', t, 'Exempel:', ex_datatyp[i], 'Förklaring:', info_datatyp[i]))
Utskrift
Datatyp: string Exempel: "@", "Johan", 'Sebastian' Förklaring: Alla texter och tecken. Markeras med dubbelfnutt(") eller enkelfnutt(') Datatyp: int Exempel: 1, 7, -9 Förklaring: Står för integer och lagrar heltal Datatyp: float Exempel: 0.0, 4.5, -9.7 Förklaring: Flyttal, lagrar decimaltal, notera decimalpunkt enligt engelskspråkig standard Datatyp: boolean Exempel: True, False Förklaring: Boolesk variabel, lagrar sant eller falskt, ofta ett resultat av ett uttryck som (1 < 2)
Uppgift: m05u08
Skapa en fil i ditt projekt som du döper till provpoäng.txt
kopiera sedan filens innehåll nedan till den filen du skapat. Filen innehåller 25 tal från 1 till 100 och lägg filen i samma mapp där du arbetar med uppgifter på moment 05. Exempelinnehållet skapas om ifall du laddar om sidan.
- Skriv ut talen sorterat från det lägsta till högsta.
- Skriv ut hur många tal som finns i listan (utgå ifrån att du inte vet hur många tal provpoäng.txt innehåller).
- Skriv ut medelvärdet av talen.
- Skriv ut medianvärdet. (Denna är lite svårare och inte obligatorisk)
När du tror att du är klar så testa att ändra data i provpoäng.txt
så att du kan få fler eller färre tal att göra dina beräkningar på. Kolla extra noga att du har en hållbar lösning för medianvärdet för jämnt och udda antal resultat. (Pseudokod underlättar lösningen här)
provpoäng.txt [klicka för att visa]
49 50 81 28 56 35 90 53 64 32 44 96 44 66 46 76 69 90 73 67 44 63 81 55 51
Hjälp [klicka för att visa]
Här kommer lite hjälp att klara av uppgiften (förutom uträkningen av medianvärdet)
- Läs in filens innehåll till en lista. Fundera på vilken datatyp du vill att datat skall ha? Skriv ut listan så att du ser att den ser ut som du vill att den skall göra.
- Inbyggda funktioner att använda till en lista hittar du i moment04.
Uppgift: m05u09 (svårare)
Gå in och redigera provpoäng.txt, du ska lägga till ett namn till varje provpoäng enligt formatet:
Sebastian|100
(Avdelaren får du med option+7 i macOS eller alt gr
+ tangenten till höger om z
i Windows.
- Skriv ut provpoängen och tillhörande elev sorterat från det lägsta till högsta.
- Skriv ut medelvärdet av provpoängen.
- Skriv ut eleven som fick det sämsta respektive bästa provresultatet.
provpoäng.txt [klicka för att visa]
Theodor|61 Maryam|28 Bianca|15 August|64 Hedvig|95 August|92 Theodor|64 Franz|43 Susan|43 Selma|66 Zoey|21 Clara|27 Noomi|76 Franz|15 Zoey|96 Rafael|16 Noomi|96 Xerxes|31 Folke|32 Theodor|25 Milo|51 Henry|16 Victoria|26 Omar|79 Leia|71
Hjälp [klicka för att visa]
Svårigheten här att hålla koll på datat så att en person hela tiden kan kopplas till rätt resultat. Detta blir extra svårt då listan av resultat skall sorteras.
- Om du skapar två olika listor så behöver resultatlistan sorteras varsamt. Om du kör den inbyggda sorteringsfunktionen så kommer endast denna lista att sorteras. Då tappar du kopplingen till listan med namn. Det är möjligt att skapa en egen sortering som sorterar resultatlistan och samtidigt gör byten av komponenter i namnlistan på samma sätt. Detta ligger dock utanför denna kurs. Men är du intresserad på hur detta kan gå till så sök efter
Bubbelsortering/Bubble sort
för att se hur man kan skapa en ganska enkel sortering på egen hand. - Ett smidigare alternativ är att använda sig av listtypen dictionary. I den listan lagras både värdet och ett index. På det sättet kan vi hålla ihop en persons resultat genom att lagra bägge i samma dictionary. Alla sådana dictionarys kan sedan läggas i en lista så att vi får en samling med alla personers resultat. Sedan finns det möjligheter att sortera denna lista på olika sätt. Se kodexempel nedan.
Kodexempel: Bygga en lista med flera dictionarys
# Importerar pprint för att få en snyggare utskrift import pprint # pp innehåller inställningen att indentera utskriften med 2 mellanslag pp = pprint.PrettyPrinter(indent=2) # Skapa en lista med 5 familjemedlemmar (dictionarys) familj = [ {'namn': 'Anders', 'ålder': 35}, {'namn': 'Doris', 'ålder': 36}, {'namn': 'Bodil', 'ålder': 5}, {'namn': 'Eva', 'ålder': 3}, {'namn': 'Claes', 'ålder': 3} ] print("\nSortera listan på ålder, ung till äldre") pp.pprint(sorted(familj, key=lambda i: i['ålder'])) print("\nSortera listan på ålder, äldre till ung") pp.pprint(sorted(familj, key=lambda i: i['ålder'], reverse=True)) print("\nSortera listan på namn, a till ö") pp.pprint(sorted(familj, key=lambda i: i['namn'])) print("\nSortera listan på först ålder, ung till äldre, och sedan på namn, a till ö") pp.pprint(sorted(familj, key=lambda i: (i['ålder'], i['namn'])))
Utskrift
Sortera listan på ålder, ung till äldre [ {'namn': 'Eva', 'ålder': 3}, {'namn': 'Claes', 'ålder': 3}, {'namn': 'Bodil', 'ålder': 5}, {'namn': 'Anders', 'ålder': 35}, {'namn': 'Doris', 'ålder': 36}] Sortera listan på ålder, äldre till ung [ {'namn': 'Doris', 'ålder': 36}, {'namn': 'Anders', 'ålder': 35}, {'namn': 'Bodil', 'ålder': 5}, {'namn': 'Eva', 'ålder': 3}, {'namn': 'Claes', 'ålder': 3}] Sortera listan på namn, a till ö [ {'namn': 'Anders', 'ålder': 35}, {'namn': 'Bodil', 'ålder': 5}, {'namn': 'Claes', 'ålder': 3}, {'namn': 'Doris', 'ålder': 36}, {'namn': 'Eva', 'ålder': 3}] Sortera listan på först ålder, ung till äldre, och sedan på namn, a till ö [ {'namn': 'Claes', 'ålder': 3}, {'namn': 'Eva', 'ålder': 3}, {'namn': 'Bodil', 'ålder': 5}, {'namn': 'Anders', 'ålder': 35}, {'namn': 'Doris', 'ålder': 36}]
5.2.2 Skriva till fil
Genomgång [klicka för att visa]
I python kan vi inte bara läsa in data från filer, vi kan också skriva till filer och skapa nya textdokument.
Vi kan i funktionen open() ange vad vi vill kunna göra med filen på följande sätt:
- och anger båda att filen öppnas i läsläge. I detta läge kan vi endast läsa från filen.
- anger att vi öppnar filen i write, skrivläge. Finns inte filen skapas en ny fil och finns filen redan skriver vi över den.
- anger att vi öppnar i skrivläge utan att skriva över befintlig data. a står för append och allt vi skriver till filen läggs till i slutet.
Kodexempel: Skriva över utskrift.txt
with open('utskrift.txt', 'w') as fw: fw.write('1 2 3 4 5\n6 7 8 9 0') with open('utskrift.txt', 'r') as fr: print(fr.read())
Utskrift
1 2 3 4 5 6 7 8 9 0
Pseudokod till kodexempel: Skriva över utskrift.txt
- Öppna eller skapa utskrift.txt om den inte existerar i skrivläge. Sätt filens storlek till 0 byte.
- Skriv '1 2 3 4 5\n6 7 8 9 0' till utskrift.txt
- Spara och stäng utskrift.txt
- Öppna utskrift.txt i läsläge
- Skriv ut allt innehåll i utskrift.txt
- Stäng utskrift.txt
Uppgift: m05u10
Förklara så noggrant du kan vad som händer i följande kod, detta görs lämpligt med kommentarer i koden. Fundera även hur utskrift.txt ser ut om den öppnas i en textredigerare efter koden körts.
Kod till uppgift m05u10
with open('utskrift.txt', 'w') as fw: fw.write('''1 2 3 4 5 6 7 8 9 ''') fw.write('\nHär var det rutigt!') with open('utskrift.txt', 'r') as fr: print(fr.read())
Problem med svenska tecken
Inom programmeringen så kan vi till och från få problem med att svenska tecken som å, ä, ö inte skrivs ut på rätt sätt. Det kan finnas olika anledningar till att det blir på det sättet, ibland är det själva inläsningen från en fil som påverkar hur dessa tecken representeras, då kan det ibland hjälpa att tala om att filen skall läsas in mha teckenkoden UTF-8
.
Kodexempel
with open('filen.txt', 'r', encoding='utf-8') as f: print(f.read())
I PyCharm kan man också välja vilken teckenkod som en viss fil skall ha, detta ser du längst ner i PyCharm. Om den är satt till UTF-8
så ökar möjligheten att filens innehåll visas korrekt. Slutligen så kan det vara så att filens teckenkod är korrekt, filen läses in på rätt sätt men att genom att du använder någon inbyggd funktion i Python inte kan hantera svenska tecken och då gör om dessa. Då kanske du behöver leta efter en annan funktion, eller så får du helt enkelt hitta en annan lösning på problemet än att använda denna funktion.
Uppgift: m05u11
Den här uppgiften använder samma information som uppgift m04u02.
Ulf har satt in 10 000kr på ett konto med 3 % årlig ränta, hur mycket pengar finns på kontot efter 15 år? Inga fler insättningar görs.
Spara följande information i en textfil för varje år:
- Det aktuella året (år 1 - 15).
- Hur mycket pengar som finns på kontot.
- Den totala procentuella utvecklingen på kontot.
Se till att använda en valfri men tydlig avdelare (delimiter) mellan varje punkt. Avsluta med att skriva ut innehållet i filen på skärmen.
Utskriftsexempel
1|10300|3.00% 2|10609|6.09% ### bortklippta rader ### 14|15126|51.26% 15|15580|55.80%
Uppgift: m05u12
Dags att bygga om/vidare på uppgift m04u08.
Låt användaren mata in heltal ett i taget, 0 avbryter inmatningen av avslutar programmet. Efter varje inmatat heltal så skall det läggas till en rad i en fil där information skall finnas om inmatat tal, högsta och lägsta tal samt summan av alla tal. Fokus på denna uppgift är att lägga till rader i befintlig fil. Använd gärna listor, inmatningskontroller och egenskapade funktioner för uppgiften.
De inbyggda funktionerna sum(), min() och max() får ej användas.
Utskriftsexempel
Inmatat tal: 3, min: 3, max: 3, summa: 3. Inmatat tal: 7, min: 3, max: 7, summa: 10. Inmatat tal: 5, min: 3, max: 7, summa: 15. Inmatat tal: 0, programmet avbryts. Hela listan: [3, 7, 5], min: 3, max: 7, summa: 15.
Uppgift: m05u13
Bygg vidare på m05u09. När du sorterat poängen, plockat ut medelvärdet (medianvärdet?) och eleverna med sämst och bäst provresultat ska all den här informationen skriva in i en textfil där svaret lagras.
För att kontrollera att allt ser bra ut ska du till slut läsa in denna textfil och skriva ut hela filens innehåll i programmet.
Uppgift: m05e02
Pelle fick i uppdrag av sin lärare att simulera ett antal yatzyslag. Tyvärr har han tappat bort koden som skapade alla slag men han har kvar filen som innehåller en mängd listor där varje lista representerar ett tärningsslag med fem tärningar. Kan du hjälpa Pelle att ta reda på hur många gånger han har slagit yatzy?
Hur många gånger borde Pelle få yatzy på de antalet gånger han har slagit?
Eftersom Pelle har tappat bort filen som han använde för att skapa alla tärningsslagen kanske du kan hjälpa honom även med denna filen? Pelle minns att kravet från läraren var att användaren skulle få frågan hur många yatzyslag man ville slå och sedan skulle det antalet slag slumpas fram.
Uppgift: m05e03
I unga programmerares kodkalender för 2020 så finns det flera roliga uppgifter. I lucka 17 så behöver du använda både funktioner och filer för att lösa uppgiften.
5.2.3 JSON
Svårare avsnitt
Detta avsnitt är lite mer utmanande än det vi gjort tidigare i detta moment. Det är dock tekniker som när ni väl har lärt er den kommer underlätta avsevärt för uppgifter som kommer göras för att visa på kunskaper för högre betyg på kursen.
Att lagra data i fil är smidigt om det är enklare data som skall lagras, men när vi behöver lagra mer komplex data så behöver vi kraftfullare tekniker till vår hjälp. Vi skall börja med att kika på ett format som heter JSON (JavaScript Object Notation) är en lagringsstruktur som används för att lagra och utbyta data mellan olika system. En stor fördel med JSON är att det är relativt lätt för våra ögon att läsa innehållet i filen. JSON har också en struktur som passar väldigt bra ihop med dictionarys då uppbyggnaden liknar varandra.
Enkelt JSON-format
{ "Name":"Anders", "Age":17 }
Här har vi info om en person, Anders som är 17 år gammal. Som du ser så är själva sättet att skriva denna informationen helt identisk med det sättet som vi använder ett dictionary
på.
Om vi nu skulle vilja spara information om flera personer så är det ju lämpligt att lagra flera dictionarys
i en lista. Skulle vi skriva detta i JSON så ser det ut på följande sätt;
[ { "Name":"Anders", "Age":17 }, { "Name":"Bodil", "Age":18 }, { "Name":"Cilla", "Age":19 }, { "Name":"David", "Age":20 } ]
Ok, då vet vi att JSON liknar de strukturer som vi redan kan använda i Python med listor och dictionarys. Då skall vi kika lite på några exempel på hur vi kan använda JSON för att lagra info till en fil och även läsa in från filen. Jag gör detta i några olika steg. Håll koll på vad som är JSON-filer (filändelse .json), vad som är pythonkod och hur utskrifterna ser ut.
5.2.3.1 Läsa från fil
Vi börjar med att läsa in från fil, då har vi först en fil som jag har döpt till teacher.json som innehåller information om en lärare. Filändelsen här är .json
men det skulle också kunna vara .txt
, eller något helt annat men det är lämpligt att döpa filerna efter innehåll.
teacher.json
{ "name": "Johan Hällgren", "qualified": true, "age": 46, "courses" : ["Programmering 1", "Programmering 2", "Webbutveckling 1", "Webbserverprogammering 1", "Tillämpad Programmering"] }
Filen innehåller information om en lärare såsom namn, ålder, vilka kurser läraren undervisar i samt om läraren har en lärarbehörighet. Kika gärna på de olika datatyperna som finns representerade här i exemplet, det finns både string, en bool, en int och en lista med strängar. JSON tar hand om detta på ett bra sätt för att vi skall kunna slippa och typkonvertera från strängar som vi är vana vid att göra när vi normalt sätt hämtar data från en fil.
För att läsa in denna text i JSON-format från filen till vårt program så behöver vi ta hjälp av det inbyggda paketet JSON som vi importerar först i koden. Sedan använder vi inbyggda funktioner i det paketet för att läsa in filen.
json.load(f)
# Importera paketet json import json # encoding för att säkra upp att å,ä,ö tolkas på rätt sätt with open("person.json", "r", encoding="utf-8") as f: # gör om filens innehåll till JSON-format t1 = json.load(f) #Skriver ut innehållet i t1 print(type(t1), t1)
Utskriften blir då;
Utskriftsexempel
{'name': 'Johan Hällgren', 'qualified': True, 'age': 46, 'courses': ['Programmering 1', 'Programmering 2', 'Webbutveckling 1', 'Webbserverprogammering 1', 'Tillämpad Programmering']}
Notera att datatypen boolean skrivs språken
så slipper du bli förvirrad.
En annan skillnad som kanske inte används lika ofta är att om vi väljer att lagra ett tomt
värde för en variabel så skrivs detta i python och i JSON. Detta används istället för "" (tom sträng) eller "0" i de fall då man inte vill använda dessa värden. definerar ett nullvärde, vilket inte är samma som tom sträng eller "0". Dessutom så kan man inte jämföra två null-värden utan man måste kolla om en variabel är genom .
Nu har vi fått se hur funktionen
fungerar. Den laddar JSON från en fil och output från funktionen blir en dictionary. Nästa funktion att hålla koll på är .json.loads(s)
s_json = '{"name": "Bo Bengtsson", "qualified": true, "age": 55, "courses": ["Matematik 1c", "Matematik 2c", "Matematik 3c"]}' t2 = json.loads(s_json) print(type(t2), t2)
Utskriftsexempel
<class 'dict'> {'name': 'Bo Bengtsson', 'qualified': True, 'age': 55, 'courses': ['Matematik 1c', 'Matematik 2c', 'Matematik 3c']}
Funktionen laddar JSON från en sträng, output blir typen dict
.
Viktigt här att bygga strängen kring enkelfnuttar (') då både index och värden i dict-strängen måste inneslutas i dubbelfnuttar ("). Lägg också märke till att i python skriver vi och i JSON skriver vid , gör du på annat sätt kommer du att få felmeddelanden.
Det var två funktioner för att läsa in data från en fil eller från en sträng till JSON. Nästa steg är att lära oss att lagra JSON till fil eller till en sträng.
5.2.3.2 Skriva till fil
Nästa funktion vi behöver hålla koll på är
där vi skriver JSON till en fil.json.dump(j, f)
# Denna koden jobbar vidare med t1 & t2 från tidigare exempel # Lagra våra två lärar-dict i en lista teachers = [] teachers.append(t1) teachers.append(t2) # Skriv ut listan till fil. file_output = open("output_list.json", "w", encoding="utf-8") json.dump(teachers, file_output, indent=4) file_output.close() # Skriv ut en lärare till fil file_output = open("output_teacher.json", "w", encoding="utf-8") json.dump(t1, file_output, indent=4) file_output.close() print(type(teachers), type(t1))
Filernas innehåll ser då ut så här;
output_list.json
[ { "name": "Johan H\u00e4llgren", "qualified": true, "age": 46, "courses": [ "Programmering 1", "Programmering 2", "Webbutveckling 1", "Webbserverprogammering 1", "Till\u00e4mpad Programmering" ] }, { "name": "Bo Bengtsson", "qualified": true, "age": 55, "courses": [ "Matematik 1c", "Matematik 2c", "Matematik 3c" ] } ]
output_teacher.json
{ "name": "Johan H\u00e4llgren", "qualified": true, "age": 46, "courses": [ "Programmering 1", "Programmering 2", "Webbutveckling 1", "Webbserverprogammering 1", "Till\u00e4mpad Programmering" ] }
Några saker att lägga märke till här, ä
är utbytt till \u00e4
som är unicode för tecknet ä
. Sedan är det viktigt att hålla koll på att om du sedan skall läsa in detta så behöver du tänka på attt innehållet i output_teacher.json
är en dictionary medan output_list.json
är en lista, så här gäller det att vara noggrann.
Slutligen skall vi kolla på funktionen som omvandlar JSON till en sträng, denna strängen kan man sedan lagra till fil eller göra något annat med.
json.dumps(j)
# Denna koden jobbar vidare med t1 & t2 från tidigare exempel # Lagra våra två lärar-dict i en lista teachers = [] teachers.append(t1) teachers.append(t2) # Gör om listan till en sträng och skriver ut. print(json.dumps(teachers)) # Gör om dictionary till en sträng och skriver ut. print("\n",json.dumps(t1))
Utskriftsexempel
[{"name": "Johan H\u00e4llgren", "qualified": true, "age": 46, "courses": ["Programmering 1", "Programmering 2", "Webbutveckling 1", "Webbserverprogammering 1", "Till\u00e4mpad Programmering"]}, {"name": "Bo Bengtsson", "qualified": true, "age": 55, "courses": ["Matematik 1c", "Matematik 2c", "Matematik 3c"]}] {"name": "Johan H\u00e4llgren", "qualified": true, "age": 46, "courses": ["Programmering 1", "Programmering 2", "Webbutveckling 1", "Webbserverprogammering 1", "Till\u00e4mpad Programmering"]}
På samma sätt som i förra varianten så är, ä
utbytt till \u00e4
som är unicode för tecknet ä
. Sedan är det viktigt att hålla koll på att om du sedan skall läsa in detta så behöver du tänka på att det ena innehållet är en lista, vilket du ser genom att innehållet finns inom list-klammrarna []
medan den andra utskriften är en dictionary.
5.2.3.3 Arbeta med dictionary / JSON
Ett okommenterat exemepel på hur man kan jobba vidare med dictionarys.
Arbeta med dictionarys
# Denna koden jobbar vidare med t1 & t2 från tidigare exempel t3 = {} # Tom dictionary t3["name"] = "Anna Andersson" t3["qualified"] = False t3["age"] = 22 t3["courses"] = [] teachers.append(t3) sum_age = 0 for t in teachers: sum_age += int(t['age']) print(t) print(f"Gemensam ålder på alla lärare: {sum_age}år.")
Utskriftsexempel
{'name': 'Johan Hällgren', 'qualified': True, 'age': 46, 'courses': ['Programmering 1', 'Programmering 2', 'Webbutveckling 1', 'Webbserverprogammering 1', 'Tillämpad Programmering']} {'name': 'Bo Bengtsson', 'qualified': True, 'age': 55, 'courses': ['Matematik 1c', 'Matematik 2c', 'Matematik 3c']} {'name': 'Anna Andersson', 'qualified': False, 'age': 22, 'courses': []} Gemensam ålder på alla lärare: 123år.
Man kan göra oerhört mycket med JSON/dictionarys/listor, det gäller bara att bestämma sig för en genomtänkt struktur. Skissa gärna på den, koden kommer inte lösa eventuella strukturella problem, de får du lösa först.
Frivillig uppgift för att träna på tekniken
På detta avsnitt finns det ingen uppgift, men som träning inför Projekt01 så kan du ju testa att göra om någon av de tidigare uppgifterna som utskrift/inläsning med JSON kopplat till filer. Lämpliga uppgifter är m05u11 för att träna på att lagra i fil. m05u12 fungerar bra att först lagra alla talen i en lista, lagra som JSON i en fil för att sedan läsa in filen och göra beräkningarna. Då får du träna på flera delar. Kanske utöka JSON med nya attribut för min, max och sum och sedan lagra även detta i en fil och skriva ut på lämpligt sätt.