Göm meny
Gäller för: HT25

Förberedelsematerial 1

Innan du börjar med Pythonuppgifterna kapitel 1-3 och inför Storseminarium 1 ska du gå igenom materialet nedan, vilket bör ta ca en timme. Du bör också genomföra det tillhörande quizet längst ner på sidan, som testar dina kunskaper på materialet. Quizet finns endast till för att du ska kunna skatta dina egna kunskaper, och är inget du bedöms på.

Innehåll

Aritmetiska operationer

Vi ska börja med att arbeta med aritmetiska operationer i Python. Detta kan göras enklast genom att jobba i den interaktiva Python tolken i terminalen. För att starta Python tolken skrivs följande i terminalen tryck sedan på enter-tangenten.

$ python3

Vi kan när som helst avsluta tolken med att trycka ctrl och d tillsammans (ctrl + d).

När vi har aktiverat Python tolken kan vi skriva in och köra Python kod direkt i terminalen. Vi kan demostrera det med en enkel addition, vi skriver in nedanstående, tryck sedan på enter-tangenten för att utföra instruktionen.

>>> 3+4

Vi borde få en utskrift 7 i terminalen. Med detta inser vi att Python kan användas som en miniräknare, men vi vill inte begränsa oss till addition. Nedanför följer några vanligt förekommande matematiska operatinoer i Python.

  • addition: +
  • subtraktion: -
  • multiplikation: *
  • division: /
  • division avrundat nedåt till närmaste heltal: //
  • modulo (rest vid nedrundningsdivision): %

När vi använder // för positiva tal så “kapar vi bort” eller trunkerar eventuella decimaler vid divisionen och vi kallar ofta lite slarvigt den här operationen för heltalsdivision. Vid “vanlig” division gäller att

$$\frac{17}{4}=4.25$$

Motsvarande heltalsdivision (17//4) ger oss istället resultatet $4$.

Lite krångligare blir det med negativa tal (det är först då som skillnaden mellan heltalsdivision och nedrundningsdivision blir meningsfull). Hur det fungerar kan man dock reda ut om man skulle behöva det.

Med modulo (%) så får vi resten vid en nedrundningsdivision enligt ovan, dvs. 17 % 4 ger oss värdet $1$.

Vi får alltså ut restvärdet vid nedanstående heltalsdivision:

$$\require{color} \frac{17}{4} = 4 \text{ rest } \colorbox{lightgreen}{1}$$

Resten är $1$ eftersom:

$$4 \times 4 + 1 = 17$$

Den matematiskt nyfikne kan notera att följande identitet gäller:

x == (x // y) * y + (x % y)

Matematik: Kongruensräkning

Modulo används oftast vid så kallad kongruensräkning där vi oftast använder följande notation:

$$17 \equiv 1 \pmod{4}$$

Kongruensräkning är vi förvånansvärt vana vid, även om ingen kanske påpekat det för oss. Det är nämligen kongruensräkning vi använder när vi arbetar med t.ex. klockan. Om klockan är 19 och vi vill veta vad klockan är om 20 timmar så säger den vanliga matematiken att klockan är 39. Med kongruensräkning kan vi dock visa att:

$$39 \equiv 15 \pmod{24}$$

I Python kan vi skriva detta som:

>>> (19 + 20) % 24
15

Litteraler och uttryck

Vi har redan arbetat med både litteraler och uttryck. En litteral är en direkt representation av ett värde t.ex 3 medan ett uttryck är allt som beräknas till ett värde, t.ex 3 + 4. Här följer några exempel:

  • 5 - 3 uttryck med två litteraler (5 och 3) och en operator (-)
  • -9 + (2 * -3) ** 2 uttryck med fyra litteraler (9, 2, 3 och 2 igen) och fem operatorer (-, +, *, - igen och **)
  • 42 uttryck med en litteral
  • "kompis" uttryck med en litteral

En viktig insikt här är att ensamma litteraler per definition också är uttryck eftersom de bara är en textuell, litteral, representation av ett värde.

Lite om Variabler

En variabel kan förstås som en symbol som refererar till ett värde. För variabelnamn används substantiv, och skrivs i små bokstäver. Om flera ord används skriver man _ mellan orden , denna konvention kallas snake case. Vi kan tilldela en variabel ett värde med en tilldelningssats t.ex:

1
zaphod_num_arms = 2

Detta är vad vi kallar en tilldelningssats, vi tilldelar värdet av litteralen 2 till zaphod_num_arms, vilket vi gör med tilldelningsoperatorn =. Vi har alltså sparat värdet $2$ i datorns arbetsminne, och kan referera till värdet med variabelnamnet zaphod_num_arms.

Fortsatt bestämmer sig Zaphod för att skaffa en tredje arm, för att förbättra sin skidboxningsteknik. Vi kan justera för detta med:

1
zaphod_num_arms = zaphod_num_arms + 1

Vi har alltså ändrat på värdet som vår variabel pekar på, från $2$, till $3$, genom att addera värdet av litteralen 1 till det tidigare sparade värdet $2$, och slutligen sparar vi det i samma variabel i vänsterledet. Kom ihåg, = är tilldelningsoperatorn. I andra sammanhang vill vi kunna spara textdata, t.ex ett efternamn:

1
zaphod_efternamn = "Beeblebrox"

eller ett decimaltal:

1
zaphods_promillehalt = 2.7

Vi vill kunna göra olika saker beroende på vilken typ av data vi jobbar med. T.ex textdata som zaphod_efternamn vill vi kunna göra om till versaler, men samma operation på zaphods_promillehalt vore orimligt. (Vad skulle det innebära att skriva 2.7 med versaler?) Därför finns det ett behov av att kunna bestämma hur vi sparar data, därav så klassificerar vi data i olika datatyper. Några vanliga datatyper som används i Python och som vi kommer att gå igenom nedan är: heltal, flyttal, strängar och listor.

Heltal och flyttal

Med heltal menas alla hela tal, negativa såväl som positiva, ex: -42 och 5. I Python kan dessa representeras med datatypen integer (int). Vi kan spara ett heltal i Python genom att tilldela en litteral 42 till vår variabel svaret_på_allt enligt nadanstående:

1
answer_for_everything = 42

Ibland vill man kunna spara tal med mer precision, det kan handla om valutor $19.90$ kr, $\pi$ ($3.14159265…$) eller som nedan, sannolikheter:

1
probability_of_ford_prefect_being_human = 0.0001

Observera att vi använder . som decimaltecken i Python. Datatypen som används för detta är float, för floating point number, eller på svenska flyttal.

Strängar

Ytterligare en datatyp är sträng (str), som representerar en följd av symboler, t.ex. "abcABC0123!%&/.". Vi kan tilldela en variabel ett värde av datatypen sträng på följande sätt:

1
galactic_advice = "Ha alltid med dig en handduk."

När vi skriver värden av datatypen sträng använder vi citationstecknen (" eller ') i början och slut för att markera början och slut på teckenföljden.

Varje tecken i följden går att referera till med en siffra, ett index, som börjar på 0 från vänster, och ökar med ett för varje tecken.

Vi kan titta på ett specifikt tecken genom att läsa det index vi är intresserade av med pythons subskriptoperator [ ].

1
galactic_advice[1]

Motsvarar tecknet 'a'.

Med negativ indexering kan vi läsa “bakifrån”.

1
galactic_advice[-2]

Vilket ger oss tecknet 'k'.

Vidare kanske vi är intresserade av att titta på en delsträng, utsnitt eller slice, av hela strängen. Vi kan använda subskriptoperatorn även för detta och skriver då:

1
my_string[start:stop]

Vi kollar på delsträngen som börjar från index start och inkluderar alla index mindre än stop. t.ex om vi har:

1
galactic_advice = "Ha alltid med dig en handduk."

och väljer ut:

1
galactic_advice[21:28]

så får vi handduk. Vi kan uppnå samma sak med negativ indexering.

1
galactic_advice[-8:-1]

Om vi utelämnar start så antar pythontolken att vi vill läsa från början av strängen. Utelämnar vi istället stop så antar pythontolken att vi vill läsa till slutet av strängen.

>>> "gargleblaster"[:6]
'gargle'
>>> "gargleblaster"[6:]
'blaster'

Fundera på vad som skulle hända om vi utelämnade både start och stop.

Typkonvertering

Ibland vill vi ha en strängrepresentation av ett värde. Med funktionen str kan vi få en strängrepresentation av vilket värde som helst, t.ex:

1
str(3.1415)

ger oss strängen "3.1415". Vidare kan vi på motsvarande sätt omvandla mellan olika datatyper. Vi kan konvertera till heltal med int eller flyttal med float. T.ex, kan vi omvandla från heltal till flyttal med:

1
float(3)

Vilket ger oss värdet 3.0.

Listor

Listor (list) används för att lagra en sekvens av värden som element i listan. En lista kan också vara ett värde, dvs en lista kan innehålla listor (som i sin tur kan innehålla listor, osv. men det återkommer vi till längre fram i kursen).

1
crew = ["Zaphod", "Ford", "Arthur", "Trillian", "Marvin"]

Listor kan indexeras på samma sätt som vi redan sätt med strängar:

1
crew[2]

motsvarar "Arthur". Samt med del-listor ex:

1
crew[:2]

ger oss ["Zaphod", "Ford"]. Med listor kan vi även blanda datatyper: strängar, heltal, flyttal, listor o.s.v. Vilket kan se ut så här:

1
ship_status = ["Hjärtat av Guld", 5, 98.7, ["Antal handdukar", 5]]

Vidare kan vi slå ihop listor med + operatorn. ex:

1
2
3
crew = ["Zaphod", "Ford", "Arthur", "Trillian", "Marvin"]
ship_status = ["Hjärtat av Guld", 5, 98.7, ["Antal handdukar", 5]]
crew + ship_status

skulle ge oss listan "Zaphod", "Ford", "Arthur", "Trillian", "Marvin", "Hjärtat av Guld", 5, 98.7, ["Antal handdukar", 5]

Vi kan dessutom läsa ut längden på listan med den inbyggda funktionen len.

1
2
3
4
5
6
# skapa en lista
crew = ["Zaphod", "Ford", "Arthur", "Tricia", "Marvin"]
# tilldela variabeln crew_size värdet som len(crew) returnerar
crew_size = len(crew)
# skriv ut värdet i variabeln crew_size
print(crew_size)

som ger oss resultatet 5.

Alla tecken på en rad efter ett #-tecken tolkas som kommentarer i Python och ignoreras av pythontolken. Detta innebär att vi kan använda dessa för att t.ex. förklara vår kod eller lämna anteckningar till andra utvecklare. Det är oftast bättre med för mycket än för lite kommentarer i er kod. Ni kommer att märka att man har stor nytta av att skriva kommentarer, det är därför fördelaktigt att ha som vana att kommentera sin kod redan från början i kursen.

Även värt att notera från ovanstående exempel är print. Detta är ytterligare en inbyggd funktion i Python, som skriver ut ett värde i terminalen. Funktionen print används ofta när man felsöker sin kod.

Villkorssatser

Används för att bestämma vilka delar av koden som ska köras, nedan visas ett exempel på en if-sats.

1
2
3
4
5
if <uttryck som beräknas till ett sanningsvärde>:
    sats1 
    sats2
    sats3
    ...

Efter raden som inleds med if och avslutas med : följer ett kodblock. Ett kodblock är ett stycke kod på samma indenteringsnivå och som tillhör någon huvudsats.

Sanningsvärden

(eng. boolean efter George Boole, i Python bool) är ytterligare en datatyp vi ska bekanta oss med. Ett sanningsvärde kan anta två olika värden, sant (True) eller falskt (False).

Men Python kan också tolka andra datatypers “sannighet” (“truthiness”) genom att sätta in ett godtyckligt värde i en kontext där pythontolken förväntar sig ett sanningsvärde. Nästan alla värden förutom värdena None, [] (den tomma listan), "" eller '' (den tomma strängen), 0 och 0.0 ses som sanna. (Det finns några till, men vi har inte gått igenom de datatyperna än, generellt kan man tänka att värden som är “tomma” eller “ingenting” tolkas som False.)

När sanningsvärdet är True körs satserna i det tillhörande blocket. När sanningsvärdet är False hoppar pythontolken över det tillhörande blocket.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
if True:
    print("Det är sant.")
if False:
    print("Det är falskt.")


det_regnar = False
det_snöar = True
if det_regnar:
    print("Det regnar!")
if det_snöar:
    print("Det snöar!")

ger oss utskriften

Det är sant.
Det snöar!

Ofta vill vi skriva mer sofistikerade villkorssatser, detta kan vi göra genom att använda jämförelse- och logiska operatorer.

Jämförelseoperatorer

Är operatorer vars beräknade värde alltid är ett sanningsvärde, dvs. True eller False. Några vanliga jämförelseoperatorer:

  • lika med: ==, x==y är sant om förhållandet $x=y$ gäller
  • inte lika med: !=, x!=y är sant om förhållandet $x\neq y$ gäller
  • större än: >, x>y är sant om förhållandet $x\gt y$ gäller
  • mindre än: <, x<y är sant om förhållandet $x\lt y$ gäller
  • större eller lika med: >=, x>=y är sant om förhållandet $x\geq y$ gäller
  • mindre eller lika med: <=, x<=y är sant om förhållandet $x\leq y$ gäller
  • samma som: is, x is y är sant om x är samma objekt som y (mer om det längre fram)
  • finns i: in, x in y är sant om x förekommer i y

Exempel:

1
2
3
4
5
6
if mina_poäng > dina_poäng:
    print("Jag vann!")
if mina_poäng == dina_poäng:
    print("Oavgjort!")
if dina_poäng > mina_poäng:
    print("Ett fel har uppstått och programmet kommer avslutas.")

Logiska operatorer

Används för att kombinera eller negera sanningsvärden. Uttryck med logisk operator beräknas alltid till ett sanningsvärde, dvs antingen True eller False precis som jämförelseoperatorer. Skillnaden är att logiska operatorer bara opererar på sanningsvärden.

  • and : ger True om båda operander är sanna.
  • or : ger True om minst en av operanderna är sanna.
  • not : ger True om operanden är False , ger False om operanden är True

Exempel: jämförelse och logiska operatorer

1
2
3
4
temperatur = -120.3
nederbörd = True
if temperatur < 0 and nederbörd:
    print("Jag tror att det snöar.")
Jag tror att det snöar.
1
2
3
4
if temperatur < 0 and not nederbörd:
    print("Det snöar i alla fall inte.")
if temperatur < -100 or temperatur > 100:
    print("Jag tror att vi är på en annan planet.")
Jag tror att vi är på en annan planet.

Olika mönster av villkorssatser

En if-sats kan bestå av flera klausuler. I det enklaste fallet har vi endast en klausul med tillhörande block som nedan.

1
2
if sanningsvärde: 
  satser

Exempel:

1
2
if outside_temperature < 0:
  print("Vi förväntar oss snö.")

Varje förekomst av nyckelordet if inleder en ny fristående if-sats, och alla if-satser på samma indenteringsdjup kommer kontrolleras oberoende av varandra. Flera sådana oberoende if -satser kan ha villkor som är sanna och blocken tillhörade dessa kommer att utföras, som i nedanstående exempel.

1
2
3
4
5
6
7
8
timmar_till_dugga = 5

if timmar_till_dugga < 24:
    print("Duggan närmar sig!")
if timmar_till_dugga < 12:
    print("Bara några timmar kvar, dags att börja plugga på allvar!")
if timmar_till_dugga < 1:
    print("Duggan är snart här! Sista minuten-plugg pågår!")

Som ger:

Duggan närmar sig!
Bara några timmar kvar, dags att börja plugga på allvar!

Ofta vill vi dock utföra viss kod när villkoret är sant men göra något annat om villkoret är falskt. Detta kan vi åstadkomma genom att lägga till en else-klausul i if-satsen. Om villkoret som tillhör if-klausulen skulle visa sig vara falskt, kommer istället blocket tillhörande else-klausulen att utföras.

1
2
3
4
if sanningsvärde:
    satser
else:
    satser

Exempel: if-sats med tillhörande else-klausul.

1
2
3
4
5
6
timmar_till_dugga = 30

if timmar_till_dugga < 24:
    print("Duggan närmar sig!")
else:
    print("Det är långt kvar till duggan...")
Det är långt kvar till duggan...

En if-sats kan bara ha en if-klausul och en else-klausul. Har vi fler än två möjliga utfall med olika villkor kan vi dock använda oss av elif-klausuler. Dessa används när vi vill kolla alternativa villkor efter varandra: om inte A -> testa B, om inte B -> testa C, om inte C -> gör D. d.v.s i nedanstående exempel utförs alltid bara ett av blocken A, B, C och D.

1
2
3
4
5
6
7
8
if condition_A:
    # gör A
elif condition_B:
    # gör B
elif condition_C:
    # gör C
else: # om inga villkor ovan gäller
    # gör D

Exempel: if-sats med tillhörande elif-klausuler och avslutande else-klausul.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
temperature = 15

if temperature < -10:
    print("Det är riktigt kallt!")
elif temperature >= -10 and temperature < 10:
    print("Det är kallt.")
elif temperature >= 10 and temperature < 20:
    print("Det är svalt.")
else:
    print("Det är varmt.")
Det är svalt.

Ett annat sätt att skriva samma sak

1
2
3
4
5
6
7
8
if temperature < -10:
    print("Det är riktigt kallt!")
elif temperature < 10:
    print("Det är kallt.")
elif temperature < 20:
    print("Det är svalt.")
else:
    print("Det är varmt.")

Detta funkar då vi kan veta efter första if satsen att temperature är större än eller likamed -10 vad vi inte vet är om temperaturen är mindre än 10 grader eller inte, osv.

elif läser vi lämpligast som ’eller om'

Funktioner

En funktion är en bit kod som syftar till att utför ett specifikt arbete, ofta bearbetar funktionen någon typ av data och ger tillbaka (returnerar) denna data. Vi kollar på ett vardagligt exempel:

Nedanför har vi definierat en funktion för att baka bröd.

  • Vi har valt att namnge vår funktion baka_bröd. Funktionsnamnet ska använda verb som beskriver arbetet.
  • Vad behöver vi för att kunna använda vår funktion? dvs för att baka bröd? Vi behöver ingredienser så som mjöl, vatten och jäst. Data som vi skickar till vår funktion kallas argument och när vi arbetar med det (i funktionen/i lådan) kallar vi det parametrar.
  • Självaste bakandet sker efter ett recept, som vi följer steg för steg, vi bearbetar parametrarna (ingredienserna) tills vi har ett färdigt bröd. Denna process utgör funktionskroppen (lådan), dvs det arbete som utförs av funktionen (att baka brödet).
  • när funktionen har kört klart, dvs när vi bakat klart, så returnerar funktionen ett värde, ett så kallat returvärde. I vårat fall får vi tillbaka ett bakat bröd från vår funktion.

Okej, men hur skriver vi funktioner i Python? Nedanför står en definition av vår funktion i Python.

1
2
3
4
5
6
def baka_bröd(mjöl, vatten, jäst):
    # här följer vi receptet
    # steg 1
    # steg 2
    # osv
    return bakat_bröd

Låt oss bryta ner vårat exempel steg för steg:

Vi börjar med att definiera vår funktion, i Python görs detta med nyckelordet def (nyckelord - ord som är inbyggda i språket Python och har en speciell innebörd som inte kan ändras). Detta följs av vårat funktionsnamn baka_bröd, till sist har vi dem argument som vi skickar till funktionen. Dessa skrivs inom parenteser ( ) och avslutas med :.

1
def baka_bröd(mjöl, vatten, jäst):

Därefter följer funktionskroppen. Funktionskroppen utgörs av ett kodblock som beskriver vad funktionen faktiskt gör, och motsvarar själva bakningen av brödet. (I exemplet ovan har vi utelämnat de faktiska operationerna.) När alla operationer som krävs för att baka brödet är genomförda så levererar vi det bakade brödet genom att returnera det.

1
return bakat_bröd

När vi skrivit instruktioner för datorn om hur man går tillväga för att baka ett bröd, men vi har inte bakat något än. För att använda vår funktion måste vi göra ett funktionsanrop. Vi börjar med att skriva namnet på vår funktion följt utav parenteser med våra argument. Nu kommer datorn att leta efter instruktionerna som vi gick igenom innan, och sätta igång och baka brödet.

1
baka_bröd(mjöl, vatten, jäst)

Men eftersom vår funktion har ett returvärde bakat_bröd, så vill vi antagligen spara det någonstans. Vi använder en variabel, och tilldelar variabeln returvärdet från vår funktion på följande vis:

1
mitt_bröd = baka_bröd(mjöl, vatten, jäst)

Efter att funktionen kört klart ersätts alltså funktionsanropet med returvärdet, vi kan visualisera det som:

1
mitt_bröd = bakat_bröd

Det här sättet att visualisera vad som händer kallas för substitution och är en värdefull metod för att förstå vad ett stycke kod gör.

obs. Alla funktioner behöver inte ha ett returvärde, i dessa fall returneras ett speciellt “icke-värde”, None, som används av Python för att beteckna just “inget värde”.

Vi har tidigare sätt hur det finns redan inbyggda funktioner i Python, t.ex: len och print. Detta innebär att vi inte behöver definiera dessa själva som vi gjorde med vår egen funktion baka_bröd. Det är också viktigt att man undvika att skriva över inbyggda funktioner, vilket sker om vi definierar vår egen funktion med samma namn, t.ex:

1
2
def print(min_text):
    ...

Detta skulle göra att vi inte längre kunde använda den inbyggda funktionen print.

Quiz

Testa din förståelse av materialet med tillhörande quiz


Sidansvarig: Johan Falkenjack
Senast uppdaterad: 2024-08-26