Föreläsningsöversikt¶
- Kort om laboration 4
- Kort om terminologilektionen 12 mars
- Parprogrammering
- Skript i verkligheten
- paket
- virtuella miljöer
- moduler, namespaces
- disposition
- defaultvärden för funktionsargument
- Bryta ut funktioner
- Mer om stränghantering och filläsning
Laboration 4¶
- Tillämpning av det mesta ni lärt er hittills
- skriva funktioner
- bearbeta listor/dictionaries
- läsa data från fil
- strukturera kod
- Rita diagram med
matplotlib
- Övning i att bryta ut funktioner och strukturera kod (Del 2)
Terminologilektion 12e mars¶
- Uppdelat i 3 pass
- 13.15 - 14.30 Y1.a och Y1.b
- 14.30 - 15.45 Y1.c och MAT
- 15.45 - 17.00 MED och TMA
- Samling i P42 på utsatt tid
- Kort genomgång av mig
- Sedan följer ni en av assistenterna till anvisad sal
Parprogrammering¶
Parprogrammering¶
- Programmeringsmetod där två programmerare programmerar tillsammans i samma stycke kod samtidigt.
- Populariserades genom utvecklingsmetodiken Extreme Programming (XP).
- Vanligt speciellt inom organisationer som jobbar med sk. agila utvecklingsmetodiker.
- XP, Scrum, Kanban, etc.
- TDDC88 - Programutvecklingsmetodik
Varför parprogrammering?¶
- Fördelar:
- Enskilda uppgifter slutförs snabbare än av en enskild programmerare
- Mindre komplex kod som är enklare att underhålla
- Speciellt komplexa problem där lösningen inte är given från start löses bättre av par
- Lagom jämna par lär sig av varandra och utvecklas snabbare
- Nackdelar:
- Fler programmerartimmar för att slutföra projekt
- $\;\;\;\;\rightarrow$ Mindre tid till testning och debuggning
- Par av noviser skriver sällan produktionsvärdig kod
Parprogrammering i undervisning¶
- Diskutera och bestäm er för vilket litet delproblem som ska göras
- Föraren jobbar på det lilla delproblemet och skriver koden
- Dvs, det är främst Föraren som har tangentbordet.
- Navigatörens jobb är att aktivt granska koden som skrivs
- Navigatörens jobb är inte att berätta för föraren exakt vad hen ska skriva, men kan komma med förslag
- Byt roller ofta! Minst en gång varje halvtimme men helst oftare, använd timer om ni tenderar att glömma.
- Ni behöver inte lösa delproblemet innan ni byter roller.
- Fira när ni löst delproblem.
Tre faser och mönster¶
- Planering: innan ni kör iväg
- Kodproduktion: när koden skrivs
- Omstart: när ni har kört fast
Planering¶
- Både förslag och genomgång av existerande kod / instruktioner
- Det är bra att säga "Jag förstår inte" eller "Jag vet inte"
- Tveka inte om att be varandra förtydliga (om ni är oeniga, fråga assistent)
- Uttryck med egna ord hur du uppfattar situationen
- Undvik fraser som "du får se om en stund", försök att förklara för din partner på en gång
- Anteckna så att ni kommer ihåg det ni kommer fram till
Kodproduktion¶
- Föraren
- berätta hur och vad du tänker
- du kan stanna upp och prata med/fråga navigatören
- undvik att bara sitta tyst och vara i din egna värld (soloprogrammering med åskådare)
- Navigatören läser koden som skrivs och säger till direkt när hen
- upptäcker enklare fel
- inte förstår vad föraren gör, namngivning, etc.
Kodproduktion¶
- Navigatören antecknar och sparar större funderingar till senare, t.ex.
- potentiella buggar (t.ex. extremvärden som kan orsaka en krasch)
- funderingar idéer på övergripande design
- saker som ni missat vid planeringen
- Det är OK att skicka tangentbordet mellan er
- vissa saker kan vara lättare för navigatören att uttrycka i kod
- byt roller om det känns bra att göra det men kom ihåg att balansera
Omstart¶
- Ni kan byta fokus och prata om annat en kort stund för att kunna tackla problemet med öppen inställning
- Gå igenom er kod se över det ni gjort
- Försök ha målet i sikte medan ni identifierar hur ni ska göra för att komma vidare
- Hitta lämpligt ställe att börja om på
- Om ni inte kommer överrens, kan det vara lämpligt att ta en kort paus, gå på toa/fika etc, lämna arbetsplatsen
- Ge varandra tid att tänka/gå igenom koden
Varning¶
- Få par är perfekt matchade.
- Om du är den (åtminstone just nu) svagare programmeraren, lämna inte över ansvaret på din partner.
- Om du är den (åtminstone just nu) starkare programmeraren, kör inte över din partner.
- Glöm inte att byta roller, men det är okej att det inte är 100 % balans, speciellt om den svagare eller mindre erfarna programmeraren får vara Förare en större del av tiden.
Vad gör vi om det inte fungerar att parprogrammera?¶
- Gör uppgifterna enskilt.
- När båda är klara: Byt kod med varandra och granska.
- Diskutera eventuella skillnader.
- Sätt ihop en gemensam slutgiltig version som båda förstår.
- Redovisa gemensam version.
- Skicka in gemensam version i Sendlab.
Skript i verkligheten¶
Skript i verkligheten¶
- moduler, namnrymder och paket
- paket
- virtuella miljöer
- moduler, namespaces
- disposition: struktur på kod, returnera och använd. mönster för skript
- defaultvärden för argument
Namnrymder¶
Namnrymd (eng. Namespace)
- Namnrymd: En samling namn (variabler, funktioner, etc.) som alla måste vara unika.
- Built-ins
- Inbyggda funktioner och variabler, t.ex.
print
ochlen
. - Tillgängliga överallt i all Python-kod.
- Inbyggda funktioner och variabler, t.ex.
- Globala namnrymden
- Alla namn som finns i den aktuella modulen, dvs. i den aktuella kodfilen.
- Dvs. alla namn vi skapat på toppnivå i modulen (dvs. utanför funktioner).
- Lokala namnrymden
- Alla namn som finns inne i en funktion när den exekveras.
- Dvs. alla parametrar och alla variabler som tilldelats inne i funktionen.
- På toppnivå i en modul gäller att globala och lokala namnrymden är samma.
Inspektera namnrymder¶
- Vi kan inspektera globala och lokala namnrymderna med funktionerna
globals
resp.locals
.- Båda funktionerna returnerar en
dict
där nycklarna är strängrepresentationer av de namn som finns i namnrymden och värdena är de värden som namnen refererar till.
- Båda funktionerna returnerar en
- På toppnivå i en modul (dvs utanför en funktionskropp) är den lokala och den globala namnrymden samma.
- Dvs om vi anropar
locals
från toppnivå i modulen (eller i interaktivt läge) får vi alltså sammadict
som om vi anropatglobals
.
- Dvs om vi anropar
Exempel: globals
och locals
på toppnivå¶
- Notera att de blir samma:
>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
Exempel: globals
och locals
i en funktion¶
- Notera att de blir olika:
>>> def my_fun(param):
... print(f"{locals()=}")
... print(f"{globals()=}")
...
>>> my_fun("arg")
locals()={'param': 'arg'}
globals()={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'my_fun': <function my_fun at 0x7fd0f6d9f380>}
Exempel: globals
och locals
i en funktion igen¶
>>> def my_fun(param):
... local_variable = "a local string"
... print(f"{globals()=}")
... print(f"{locals()=}")
...
>>> my_fun("arg")
globals()={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'my_fun': <function my_fun at 0x7fd0f6d9f380>}
locals()={'param': 'arg', 'local_variable': 'a local string'}
Import av modul
- När vi importerar en modul med en vanlig
import
-sats så binder vi dess namnrymd till ett prefix i den lokala namnrymden.- (Ett prefix är bara en variabel vars värde är en sådan namnrymd.)
import random
binder namnrymdenrandom
till prefixetrandom
.- Vi kan nu komma åt namnrymden med hjälp av prefixet, t.ex.
In [ ]:
import random
print(random.randint(1, 10))
- Testa att köra
print(globals())
före och efter import-satsen.
Exempel: Efter import
i en funktion¶
>>> def my_fun(param):
... import math
... print(f"{locals()=}")
... print(f"{globals()=}")
...
>>> my_fun("arg")
locals()={'param': 'arg', 'math': <module 'math' from '/home/johsj47/miniconda3/envs/py3_11/lib/python3.11/lib-dynload/math.cpython-311-x86_64-linux-gnu.so'>}
globals()={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'my_fun': <function my_fun at 0x7fd0f6d9f560>}
Paket
- Paket är ett sätt att strukturera större namnrymder hierarkiskt.
- Paket består av moduler och underpaket (som i sin tur kan bestå av moduler och underpaket, osv.)
- Vi kan jämföra ett paket med en katalogstruktur.
- Ofta används paket också synonymt med verktyg som skapats av andra Python-utvecklare och som utökar Python med ny funktionalitet.
- Man kan jämföra dessa med expansioner eller DLCs till spel (fast oftast är de gratis och skapade av tredjepartsutvecklare, så det är kanske mer korrekt att jämföra dem med moddar).
- Paketet
requests
som ni installerade i Lektion 3 är ett sådant paket. - På PyPI (Python Package Index) kan man hitta alla sådana paket som kan installeras med
pip
.
Paket för Python¶
- Det finns många fritt tillgängliga paket att använda med Python (se t.ex. PyPI - the Python Package Index).
- Finns ingen anledning att återuppfinna hjulet när det finns färdiga hjul att installera och använda.
- (I riktiga projekt, dvs. Det finns självklart goda pedagogiska skäl att återuppfinna alla möjliga olika hjul.)
- Exempel
- Requests (hämta data från webben). https://requests.readthedocs.io/en/master/
- NumPy (matrisberäkningar m.m.). https://numpy.org/
- TensorFlow (maskininlärning). https://www.tensorflow.org/
- Pandas (databearbetning). https://pandas.pydata.org/
- Pillow (bildbehandling). https://python-pillow.org/
- Flask (webbserver). https://palletsprojects.com/p/flask/
- Beautiful Soup (extrahera data från webbsidor). https://www.crummy.com/software/BeautifulSoup/
- OpenCV (bildbehandling). https://docs.opencv.org/master/index.html
Installera paket (repetition)¶
- Enkelt att installera paket:
$ python3 -m pip install <namn på paket>
- eller
$ pip3 install <namn på paket>
- (Detta bör ni ha gjort med
requests
under Lektion 3.)
Alias och underpaket
- När vi importerar kan vi använda
as
för att skapa ett lokalt alias, dvs ett alternativt prefix.- Ett sådant alias kan vara vad som helst, men det finns ofta starkt etablerade konventioner.
- När vi skapar ett alias kan vi använda punktnotation för att importera bara ett specifikt underpaket:
import matplotlib.pyplot as plt
Några standardalias man kan stöta på¶
- Inbyggda moduler
MODULE ALIAS IMPORT STATEMENT
datetime dt import datetime as dt
multiprocessing mp import multiprocessing as mp
- Externa paket
NumPy np import numpy as np
SciPy sp import scipy as sp
Pandas pd import pandas as pd
Seaborn sns import seaborn as sns
TensorFlow tf import tensorflow as tf
Tkinter tk import tkinter as tk
Importera till lokala namnrymden
- Vi kan importera enskilda namn till den lokala namnrymden med hjälp av nyckelordet
from
, t.ex:
from math import sqrt
- Vi behöver inget prefix, men varning för namnkonflikter.
In [1]:
# import the randint function from the random module to the local namespace
from random import randint, choice
# import the plot function from the matplotlib.pyplot module to the local namespace
from matplotlib.pyplot import plot
print(f"{randint(0, 100)=}")
print(f"{choice(['a', 'b', 'c'])=}")
# anrop till funktionen plot() från modulen matplotlib.pyplot
print(f"{plot([0, 1, 2, 3])=}")
randint(0, 100)=13 choice(['a', 'b', 'c'])='b' plot([0, 1, 2, 3])=[<matplotlib.lines.Line2D object at 0x7f032e2248e0>]
Varning!!¶
- Använd inte konstruktionen
from modulename import *
- Stor risk för namnkonflikter.
- Importerade namn kan överskugga (eng. shadow) inbyggda och globala namn och orsaka svårhittade buggar.
- Exempel:
>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
>>> from math import *
>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'acos': <built-in function acos>, 'acosh': <built-in function acosh>, 'asin': <built-in function asin>, 'asinh': <built-in function asinh>, 'atan': <built-in function atan>, 'atan2': <built-in function atan2>, 'atanh': <built-in function atanh>, 'cbrt': <built-in function cbrt>, 'ceil': <built-in function ceil>, 'copysign': <built-in function copysign>, 'cos': <built-in function cos>, 'cosh': <built-in function cosh>, 'degrees': <built-in function degrees>, 'dist': <built-in function dist>, 'erf': <built-in function erf>, 'erfc': <built-in function erfc>, 'exp': <built-in function exp>, 'exp2': <built-in function exp2>, 'expm1': <built-in function expm1>, 'fabs': <built-in function fabs>, 'factorial': <built-in function factorial>, 'floor': <built-in function floor>, 'fmod': <built-in function fmod>, 'frexp': <built-in function frexp>, 'fsum': <built-in function fsum>, 'gamma': <built-in function gamma>, 'gcd': <built-in function gcd>, 'hypot': <built-in function hypot>, 'isclose': <built-in function isclose>, 'isfinite': <built-in function isfinite>, 'isinf': <built-in function isinf>, 'isnan': <built-in function isnan>, 'isqrt': <built-in function isqrt>, 'lcm': <built-in function lcm>, 'ldexp': <built-in function ldexp>, 'lgamma': <built-in function lgamma>, 'log': <built-in function log>, 'log1p': <built-in function log1p>, 'log10': <built-in function log10>, 'log2': <built-in function log2>, 'modf': <built-in function modf>, 'pow': <built-in function pow>, 'radians': <built-in function radians>, 'remainder': <built-in function remainder>, 'sin': <built-in function sin>, 'sinh': <built-in function sinh>, 'sqrt': <built-in function sqrt>, 'tan': <built-in function tan>, 'tanh': <built-in function tanh>, 'trunc': <built-in function trunc>, 'prod': <built-in function prod>, 'perm': <built-in function perm>, 'comb': <built-in function comb>, 'nextafter': <built-in function nextafter>, 'ulp': <built-in function ulp>, 'pi': 3.141592653589793, 'e': 2.718281828459045, 'tau': 6.283185307179586, 'inf': inf, 'nan': nan}
Använda pythonfil som modul¶
- Filer med pythonkod är moduler
- I filen
uppg1.py
:
def load_csv(filename):
# satser i funktionen
- Import och användning av
uppg1.py
som modul från annan pythonfil i samma katalog
import uppg1
uppg1.load_csv("data.csv")
Vad händer när man importerar en modul?¶
- Vid import körs alla rader från filen som importeras.
- Eventuella problem
- vi vill att viss kod körs när vi använder filen som ett skript
- vi vill inte att skript-koden körs när vi importerar filen som en modul
- Exempel
- Filen
pokedex.py
innehåller kod som kan användas för ta fram information om en Pokémon via PokeAPI - När
pokedex.py
körs kommer den att titta på argumenten som skriptet fått - Vi skriver ett annat program som ska använda sig av Pokémon-data, vi vill därför använda funktionerna i
pokedex.py
, men vill inte att pokedex-skriptet körs.
- Filen
Den nuvarande namnrymnden¶
- Speciella variabeln
__name__
innehåller namnet på namnrymden för koden som körs - Namnrymden för ett skript (huvudprogram) som körs är
"__main__"
- Namnrymden för en kod i en modul är namnet på modulen
- Exempelanvändning: kör inte viss kod när en fil importeras:
# om vi kör filen som ett skript, kör programmet
# annars utvärderas endast funktionerna m.m. i filen
if __name__ == "__main__":
main()
utskrifter.py
¶
In [ ]:
print(f"\n{'#'*10} utskrifter.py körs {'#'*10}")
print(f"* __name__ i början av utskrifter.py: {__name__}")
def skriv_ut_namnrymd():
print(f"* __name__ i funktionen skriv_ut_namnrymd(): {__name__}")
print("\nAnropar skriv_ut_namnrymd() från utskrifter.py")
skriv_ut_namnrymd()
if __name__ == "__main__":
print("\n__name__ har värdet '__main__' i utskrifter.py, kör skriv_ut_namnrymd()!")
skriv_ut_namnrymd()
print(f"{'#'*10} utskrifter.py klar {'#'*10}\n")
skript.py
¶
In [ ]:
#!/usr/bin/env python3
print(f"\n{'#'*10} skript.py körs {'#'*10}")
import utskrifter
print("__name__ i skript.py: {}".format(__name__))
print("Anropar utskrifter.skriv_ut_namnrymd() från skript.py")
utskrifter.skriv_ut_namnrymd()
if __name__ == "__main__":
print("Anropar utskrifter.skriv_ut_namnrymd() IGEN från skript.py")
utskrifter.skriv_ut_namnrymd()
print(f"\n{'#'*10} skript.py klar {'#'*10}\n")
Virtuella miljöer¶

Problem vid installation av paket¶
- Vissa projekt ska köras i miljöer med begränsade resurser
- Dvs vi kan inte installera hur många onödiga paket som helst
- Olika projekt kan behöva olika versioner av samma paket
- Rättigheter kan saknas för att installera paket på systemnivå
- Lösning
- Användar- och/eller projektspecifika virtuella miljöer med t.ex. modulen
venv
- Användar- och/eller projektspecifika virtuella miljöer med t.ex. modulen
- Överkurs (alternativ till
venv
som inte är inbyggda i Python):pyenv
virtualenv
(tredjepartsbibliotek som inspireratvenv
)pipenv
poetry
conda
venv
- inbyggd modul¶
- Skapar virtuell Python-miljö som kan aktiveras/avaktiveras.
- Olika virtuella miljöer kan skapas för olika uppsättningar av Python-paket.
- Endast virtuella miljöer för aktuell version av Python 3 kan skapas, t.ex. 3.10.12 på LiUs system.
- (Med överkursverktygen kan även flera olika Python-versioner existera parallellt.)
venv
¶
- Skapa virtuell miljö:
$ python3 -m venv <katalog>
- Aktivera virtuell miljö
$ source venv_mittprojekt/bin/activate
- Av-aktivera virtuell miljö
$ deactivate
- Installera paket i aktiv miljö
$ python3 -m pip install <paket>
Är det inte bättre att installera allt man behöver i en och samma miljö?¶
- I verkligheten jobbar man ofta parallellt i flera olika projekt.
- Alla projekt kan inte ha en produktionsmiljö där där massor onödiga saker finns.
- Utvecklingsmiljön bör spegla produktionsmiljön i möjligaste mån.
Vanlig disposition för skript¶
- importer
- globala variabler
- funktioner
- kod utanför funktioner (ofta i ett
__name__ == "__main__"
-block).
In [3]:
import math
PRECISION = 3
def rounded_sqrt(value):
return round(math.sqrt(value), PRECISION)
if __name__ == "__main__":
print(rounded_sqrt(float(input(">>> "))))
>>> 5 2.236
Default-värden för parametrar¶
Default-värden¶
- Funktioner med valfria argument; parametrar med defaultvärden
- Används när man vill ha möjligheten att ange fler argument för att ändra en funktions beteende, men vill göra det möjligt att använda funktionen utan att ange alla argument
- Kommer att användas av många av funktionerna i
matplotlib
(Laboration 4, Del 1) - Exempel
open(filnamn) # är samma sak som
open(filnamn, 'r', -1, None, None, None, True, None)
Default-värden¶
- Syntax
In [ ]:
def funktionsnamn(param1, param2=10, param3=20):
print(f"param1: {param1}, param2: {param2}, param3: {param3}")
- Argument utan default-värde måste alltid anges
- Argument med default-värde kan hoppas över eller anges explicit (nyckelordsargument)
- Positionella argument måste användas före nyckelordsargument.
In [ ]:
funktionsnamn(1, param3=2)
Funktionsdefinitioner, defaultvärden¶
In [ ]:
def print_greeting1(name):
print(f"Hej {name}!")
def print_greeting2(name="Guido"):
print(f"Hej {name}!")
def print_greeting3(name="Eggbert", greeting="Hej"):
print(f"{greeting} {name}!")
print_greeting1("Yoda")
print_greeting2()
print_greeting3(greeting="Tjena")
print_greeting3("Tjena")
print_greeting3("Eggbert", greeting="Tjena")
Använd inte muterbara värden som defaultvärden!¶
In [17]:
def get_short_names(name_candidates, short_names=[]):
for name in name_candidates:
if len(name) < 4:
short_names.append(name)
return short_names
In [25]:
print(get_short_names(["1234", "12345", "12"]))
['12', 'ab', 'ab', 'ab', 'ab', '12', '12', '12']
In [26]:
print(get_short_names(["abcd", "abcde", "ab"]))
['12', 'ab', 'ab', 'ab', 'ab', '12', '12', '12', 'ab']
Lösning: använd istället None
som defaultvärde, hantera i funktionen¶
In [ ]:
def get_short_names(name_candidates, short_names=None):
if short_names is None:
short_names = []
for name in name_candidates:
if len(name) < 4:
short_names.append(name)
return short_names
In [ ]:
print(get_short_names(["1234", "12345", "12"]))
In [ ]:
print(get_short_names(["abcd", "abcde", "ab"]))
Bryta ut funktioner¶
Varför bryta ut funktion?¶
- Minska upprepad kod
- lättare att underhålla kod och uppdatera kod
- lättare att läsa kod, inte lika lång
- Minska väldigt snarlik kod
- ersätt snarlika kodavsnitt med funktion som anpassar beteende givet argumenten
- Underlätta läsning av kod – givet att bra funktionsnamn används.
Bryta ut funktioner, saker att tänka på¶
- Scope och namespace: globala variabler och lokala variabler
- Lokala variabler
- alla variabler som definieras inne i en funktion (inkl. argumenten) är bara tillgängliga inuti funktionen
- Globala variabler
- variabler som definieras utanför funktioner är globala
- om de ska användas inuti en funktion bör nyckelordet
global
användas
- Undvik att använda globala variabler. Konstanter är ok, använd i så fall variabelnamn med endast versaler
Namngivning¶
- Bra namn hjälper läsaren och programmeraren förstå vad koden gör.
- Dåliga namn är intetsägande eller, ännu värre, vilseledande.
- Använd verbliknande namn till funktioner
show_commands()
,get_shortest_name()
,load_data()
- Använd substantivliknande namn till variabler; singular om det är ett värde, plural om det handlar flera värden (t.ex. listor och dictionaries)
command
,commands
,name
,names
Exempel, scope¶
- Globala och lokala variabler:
localglobal.py
- Varför blir det fel:
localvars.py
- Varför blir det fel:
globalvars.py
Bryta ut funktioner, exempel på tillvägagångssätt¶
- Ta kod som upprepas och stoppa in den i en funktion
- Lägg till argument till funktionen efter behov.
- Byt ut upprepande kod mot anrop till funktionen du skapat.
- Vid parprogrammering är en typisk uppgift för navigatören att påpeka när kod bör brytas ut till en egen funktion.
In [ ]:
def treasure_finder():
map1 = [55, 94, 39, 38, 57, 25, 53, 13, 57, 51]
map2 = [71, 44, 98, 26, 18, 64, 37, 91, 81, 14]
treasure_value = 81
treasure_found = False
pos = 0
while pos < len(map1):
if map1[pos] == treasure_value:
treasure_found = True
break
pos += 1
if treasure_found:
print(f"Yay! Found treasure in map1 at position {pos}!")
else:
print("No treasure found in map1 :(")
treasure_found = False
pos = 0
while pos < len(map2):
if map2[pos] == treasure_value:
treasure_found = True
break
pos += 1
if treasure_found:
print(f"Yay! Found treasure in map2 at position {pos}!")
else:
print("No treasure found in map2 :(")
treasure_finder()
In [ ]:
def get_treasure_position(lst, treasure_value):
pos = 0
while pos < len(lst):
if lst[pos] == treasure_value:
return pos
pos += 1
return -1
def print_result(map_name, pos):
if pos >= 0:
print(f"Yay! Found treasure in {map_name} at position {pos}!")
else:
print(f"No treasure found in {map_name} :(")
def treasure_finder():
map1 = [55, 94, 39, 38, 57, 25, 53, 13, 57, 51]
map2 = [71, 44, 98, 26, 18, 64, 37, 91, 81, 14]
treasure_value = 81
treasure_pos = get_treasure_position(map1, treasure_value)
print_result("map1", treasure_pos)
treasure_pos = get_treasure_position(map2, treasure_value)
print_result("map2", treasure_pos)
treasure_finder()
Riktlinjer för vad och hur man ska bryta ut¶
- Försöka att se till att varje funktion endast gör en sak.
- Försök att hålla funktioner korta (olika beroende på kodstil).
- Ex. ~10 rader.
- Låt funktioner ha verbliknande namn.
- Låt variabler ha substantivliknande namn.
- Använd förklarande namn.
- Använd namn i plural för listor och dictionaries.
Bra heurestiker för att bryta ut funktioner¶
- Försök se till så att en funktion bara gör en sak.
- Är det svårt att skriva en lättförståelig men utförlig dokumentationssträng?
- I så fall bör funktionen antagligen brytas upp i mindre delar som kan beskrivas enkelt.
- Sätt en maxlängd på hur många rader en funktion får ha.
- Sätt ett maxdjup på hur många nivåer av indentering en funktion får ha.
Återbesök: filläsning och stränghantering¶
Läsa från textfil¶
- Öppna fil:
file = open(filename)
- Läs rader från öppen fil:
lines = file.readlines()
- Stäng öppen fil:
file.close()
- Bearbeta rader: t.ex.
for line in lines:
- Viktigt: Vad för datastruktur får vi från
file.readlines()
?
with
-satsen - ett bättre sätt att öppna filer¶
with
-satsen används för att förenkla resurs- och felhantering.- De flesta användningsområdena för
with
ligger utanför den här kursens omfattning, men:
with open('file_path') as file:
lines = file.readlines()
with
-satsen hanterar automatiskt stängning av öppnade filer när blocket avslutas, även om programmet skulle råka krascha.
Praktiska strängmetoder¶
str.split()
¶
- Skapar en lista av delsträngar, skapade genom att dela strängen vid en viss sekvens (blanktecken som default)
- Blanktecken (eng. whitespace) representerar något av de olika blanka tecken som förekommer i text, t.ex. mellanslag, radbrytning och tab.
str.split()
In [ ]:
"""hej, hopp, nepp""".split()
- För att ange en specifik sekvens som ska dela strängen skickar man med ett argument till den första parametern,
sep
.- Dvs.
str.split(separator)
ellerstr.split(sep=separator)
- Dvs.
In [ ]:
"hej, hopp, nepp".split(", ")
str.join(lista)
¶
- Eftersom strängar är immutable så är det ineffektivt att genomföra många konkateneringar på raken.
- Istället kan vi använda
str.join(lista)
som tar en lista av delsträngar och slår ihop dem till en sträng med strängen som metoden anropas på mellan varje delsträng.
In [ ]:
"".join(["hej", "hopp", "nepp"])
In [ ]:
" - ".join(["hej", "hopp", "nepp"])
str.join(lista)
, forts.¶
- För att stegvis bygga upp en sträng i en loop kan vi alltså ersätta:
result = ""
for string in iterable:
result += string
- med:
partials = []
for string in iterable:
partials.append(string)
result = "".join([partials])
- Vilket, om vi ska göra många konkateneringar, kan ha mycket stor betydelse för hur snabbt programmet kör.
str.join(lista)
, exempel¶
- En funktion som givet en sträng och en dictionary med ersättningsregler returnerar en ny sträng med tecken utbytta enligt reglerna.
- Reglerna anges som t.ex.
{'a': 'ä', 'o': 'ö'}
för att byta ut alla 'a' mot 'ä' och alla 'o' mot 'ö'
- Reglerna anges som t.ex.
In [30]:
def replacer(original, rules):
partials = []
for char in original:
if char in rules.keys():
partials.append(rules[char])
else:
partials.append(char)
return "".join(partials)
Jämförelse mellan direkt konkatenering och str.join()
¶
In [27]:
def replacer_concat(original, rules):
result = ""
for char in original:
if char in rules.keys():
result += rules[char]
else:
result += char
return result
In [32]:
import timeit
test_var = 'nästäppa'*100000
In [33]:
print("concat: ", timeit.timeit("replacer_concat(test_var, {'t': 'd', 'p': 'b'})", number=100, globals=globals()))
print("join: ", timeit.timeit("replacer(test_var, {'t': 'd', 'p': 'b'})", number=100, globals=globals()))
concat: 7.02377389796311 join: 5.890890292997938
Ännu snabbare lösning med split
och join
¶
In [34]:
def replacer_split_inplace(original, rules):
result = original.split()
for i, char in enumerate(result):
if char in rules.keys():
result[i] = rules[char]
return ''.join(result)
In [35]:
print("concat: ", timeit.timeit("replacer_concat(test_var, {'t': 'd', 'p': 'b'})", number=100, globals=globals()))
print("join: ", timeit.timeit("replacer(test_var, {'t': 'd', 'p': 'b'})", number=100, globals=globals()))
print("inplace: ", timeit.timeit("replacer_split_inplace(test_var, {'t': 'd', 'p': 'b'})", number=100, globals=globals()))
concat: 7.28358013095567 join: 6.345866783056408 inplace: 0.0842719619977288
str.strip()
, str.lstrip()
, str.rstrip()
¶
- Ta bort alla blanktecken i början och/eller slutet av strängar.
- str.rstrip()
- r för right, dvs tar bort blanktecken till höger, dvs i slutet av strängen
- str.lstrip()
- l för left, dvs tar bort blanktecken till vänster, dvs i början av strängen
- str.strip()
- Tar bort blanktecken i både början och slutet.
str.strip()
, str.lstrip()
, str.rstrip()
exempel¶
In [ ]:
" hej vad heter du? \n".rstrip()
In [ ]:
" hej vad heter du? \n".lstrip()
In [ ]:
" hej vad heter du? \n".strip()
Läsa från textfil¶
- Öppna fil
with open('file_path') as datafile:
- Läs rader från öppen fil, t.ex.
for row in datafile:
- eller
datarows = datafile.readlines()
for row in datarows:
- För varje rad, bearbeta raden, t.ex.
- hoppa över rad (använd
continue
) - ta bort radbryt från slutet av raden med
str.rstrip()
- konvertera från sträng till annan datatyp
- dela upp raden med
str.split()
- stoppa in värden i lista
- hoppa över rad (använd