Föreläsningsöversikt (4.1-4.2)¶
- Om Tema 4-6
- Datalogiskt tänkande (Computational Thinking)
- Algoritmer
- Komplexitet
- Objektorienterad programmering
- terminologi
- använda objekt
- Programmeringsmetod
Temaupplägg, Tema 4-6¶
- Temauppgift: Tillämpning av ny syntax och begrepp. (LAB2)
- 1 eller 3 poäng per temauppgift.
- Lektion: Förberedelse inför temauppgift.
- Seminarium: Genomgång och diskussion av temauppgiften i grupp. (EXA2)
- Rapport: Skriftlig behandling av aspekter av temauppgiften. (EXA2)
- 1-3 poäng per rapport.
Examination: resultat på delmoment¶
LAB2. 4,5hp: Kod för Temauppgift 4-6
EXA2. 1,5hp: Seminarier och Rapporter
Betyg LAB2
- För betyget Godkänd på LAB2 ska alla temauppgifter vara godkända (1 poäng).
- För betyget Väl godkänd på LAB2 krävs minst 7 poäng totalt från temauppgifterna.
Betyg EXA2
- För betyget Godkänd på EXA2 ska alla algoritmseminarier och algoritmrapporter vara inlämnade och godkända (1 poäng).
- För betyget Väl godkänd på EXA2 behövs minst 7 poäng totalt från rapporterna.
För VG på kursen: VG på LAB1, LAB2 och EXA2
Alla uppgifter i Tema 4-6 görs i par¶
- Ni skall byta labbpartner.
- Slå er ihop med någon på samma nivå och med samma betygsambition.
- Anmäl er till en pargrupp i LAB2
- Anmäl er till seminariegrupp i EXA2
- Samma grupp i både LAB2 och EXA2 (t.ex. A3 i LAB2 och A3 i EXA2)
- Rekommendation: Anmäl er i en grupp med en annan assistent för seminarier, i den mån det är möjligt med en ny partner.
- Deadline för anmälan 1 november
Övergripande läromål för Tema 4-6¶
- Datalogiskt tänkande:
- Abstraktion och dekomposition.
- Algoritmer och mönsterigenkänning.
- Programmering:
- Objektorienterad modellering och design
- Syntax i Python
- Programmeringsmetod (implementation, testning, felsökning)
Tema 4-6¶
- Tema 4: Sorteringalgoritmer och objekt
- Introduktion till datalogiskt tänkande och objektorientering
- Sorteringsalgoritmer
- Tema 5: Grafiska gränssnitt och layout
- Introduktion till GUI-programmering
- Algoritm för layout
- Tema 6: Eget att-göra-program
- Introduktion till objektorienterad design
- Objektorienterat program jämfört med icke-objektorienterat program
Datalogiskt tänkande¶
Vad är ett program?¶
"We are about to study the idea of a computational process. Computational processes are abstract beings that inhabit computers. As they evolve, processes manipulate other abstract things called data. The evolution of a process is directed by a pattern of rules called a program. People create programs to direct processes. In effect, we conjure the spirits of the computer with our spells."
--- Harold Abelson, Gerald Sussman, and Julie Sussman (1996) Structure and Interpretation of Computer Programs (2nd. ed.) McGraw-Hill, Inc., USA. (aka The Wizard Book)
Computational Thinking¶
"Computational thinking is the thought processes involved in formulating problems and their solutions so that the solutions are represented in a form that can be effectively carried out by an information-processing agent."
--- Wing, Jeannette M. (2011) Research Notebook: Computational Thinking—What and Why? The LINK. The Magazine of Carnegie Mellon University's School of Computer Science. Carnegie Mellon University, School of Computer Science.
Sammanfattningsvis¶
Datalogiskt tänkande är…
Ett sätt att tänka ut hur den ”beräkningsmässiga process” som Abelson och Sussman pratade om ska se ut, och…
Ett sätt att tänka ut hur program som beskriver den processen bör organiseras.
Är inte det samma sak som programmering?¶
- Programmering är det praktiska hantverket kring att realisera datalogiskt tänkande som faktiska program.
- Datalogiskt tänkande kan existera utan programmering.
- Programmering kan existera utan datalogiskt tänkande, men inte med någon större framgång i det långa loppet.
Datalogiskt tänkande är inte…¶
- Att tänka i ettor och nollor.
- Vi måste dock ibland ta hänsyn till att datorn gör det
- Att tänka exakt som en dator.
- Vi måste dock förstå hur datorn ”tänker”.
- Att manuellt utföra en given algoritm (t.ex. att följa Kruskals algoritm på papper).
- Vi måste dock förstå hur sådana algoritmer konstrueras och används för att lösa problem.
- Datorer som tänker, dvs artificiell intelligens.
- Men när man utvecklar AI-mjukvaror är datalogiskt tänkande självklart en del av processen.
- Datalogiskt tänkande är även tillämpbart för ”prompt engineering” när man använder generativ AI.
Ett exempelproblem¶
- I Temauppgift 2 byggde vi ett system för autocompletion. Det systemet byggde på frekvensinformation. Nu ska vi, givet ett korpus av text lagrat i en textfil, skapa sådan frekvensdata.
- Texten vi ska utgå ifrån är Moby Dick av Herman Melville
- Ord med olika kapitaliseringar skall räknas som lika, dvs "Hej" ska vara lika med "hej".
- Textfilen vi har fått är redan tokeniserad med följande egenskaper:
- Tokeniseringen har delat upp texten så att varje stycke står på en egen rad, och varje token (ord eller skiljetecken) är omringat av mellanslag. Tokeniseringen gör att vissa grammatiska strukturer står för sig. Till exempel blir strängen "it's" efter tokenisering "it ' s", och både "it" och "s" kan tolkas som ord.
- I det här fallet har vi beslutat att det är okej för dessa att tolkas som ord.
Första stycket från Moby Dick¶
Call me Ishmael . Some years ago - never mind how long precisely - having little or no money in my purse , and nothing particular to interest me on shore , I thought I would sail about a little and see the watery part of the world . It is a way I have of driving off the spleen and regulating the circulation . Whenever I find myself growing grim about the mouth ; whenever it is a damp , drizzly November in my soul ; whenever I find myself involuntarily pausing before coffin warehouses , and bringing up the rear of every funeral I meet ; and especially whenever my hypos get such an upper hand of me , that it requires a strong moral principle to prevent me from deliberately stepping into the street , and methodically knocking people ' s hats off - then , I account it high time to get to sea as soon as I can . This is my substitute for pistol and ball . With a philosophical flourish Cato throws himself upon his sword ; I quietly take to the ship . There is nothing surprising in this . If they but knew it , almost all men in their degree , some time or other , cherish very nearly the same feelings towards the ocean with me .
Vad behöver vi för att kunna lösa den här uppgiften?¶
Ett utkast¶
def calculate_word_frequencies_in_file(textfile):
pass
Dekomposition¶
Vi har precis delat upp ett svårt problem i mindre, mer hanterbara delproblem.
Delproblem kan i sin tur behöva delas upp i ännu mindre delproblem.
Sådan nedbrytning av problem kallas för dekomposition.
Dekomposition på flera nivåer¶
- Dela upp sin lösning i komponenter med ansvar för olika saker.
- T.ex. gränssnitt, datalagring, nätverk, databearbetning
- Dela upp en sådan komponent i funktioner som gör olika saker.
- T.ex. en funktion som skickar data över ett nätverk, en annan funktion som hämtar data från en nätverkskälla, etc.
- Dela upp en funktion i en serie diskreta operationer.
- T.ex. basfall och rekursionssteg i en rekursiv funktion.
- Dekomposition kan alltså ses som en rekursiv process.
Hur små delar behöver man bryta upp problem i?¶
- Beror på vilka byggstenar vi har när vi ska lösa ett visst problem.
Vilka byggstenar har vi då?
De som redan är implementerade av oss eller någon annan i det projekt vi arbetar.
Primitiver.
Primitiver¶
- Inbyggda komponenter i det programmeringsspråk vi använder.
- Vilka primitiver finns i Python?
Tillbaka till vårt utkast¶
def calculate_word_frequencies_in_file(textfile):
tokenized_text = get_text_from_file(textfile)
token_list = get_token_list(tokenized_text)
for token in token_list:
count(token)
return frequencies
- Är detta bästa möjliga uppdelningen?
- Kommer vi alltid bara vilja räkna ordfrekvenser i filer? Tänk om texten hämtas från webben istället?
En annan uppdelning¶
def calculate_word_frequencies(tokenized_text):
token_list = get_token_list(tokenized_text)
for token in token_list:
count(token)
return frequencies
def calculate_word_frequencies_in_file(textfile):
tokenized_text = get_text_from_file(textfile)
return calculate_word_frequencies(tokenized_text)
Abstraktion¶
- Här har vi abstraherat själva beräkningen av ordfrekvenser från att beräkna ordfrekvenser för en textfil.
- Oavsett varifrån vi får texten kommer vi kunna beräkna ordfrekvenser med
calculate_word_frequencies
. - Även
get_text_from_file
kan betraktas som en abstraktion, av själva filinläsningen. calculate_word_frequencies_in_file
knyter ihop de båda abstraktionerna och bildar en ny abstraktionsnivå.
Abstraktionsnivå¶
- Vi pratar ofta om att vi rör oss uppåt eller neråt i abstraktionsnivå.
- Genom att skapa en ny abstraktion går vi upp i abstraktionsnivå.
- Genom att titta på hur en abstraktion är implementerad går vi ner i abstraktionsnivå.
- Alla abstraktioner är sammansatta av primitiver och andra lägre abstraktioner.
- Även abstraktioner kan alltså i någon mening betraktas som rekursiva.
- Abstraktioner består av abstraktioner som består av abstraktioner... hela vägen ner till primitiverna.
Abstraktioner hjälper oss att tänka¶
- Behöver inte tänka på alla detaljer utan vi kan arbeta med abstraktionen i sin helhet.
- Vi vet att
get_text_from_file(moby_dick_tokenized.txt)
kommer returnera texten imoby_dick_tokenized.txt
, hurget_text_from_file
åstadkommer det behöver vi inte fundera på.
- Vi vet att
- Låter oss lyfta blicken och fokusera på större problem som vi vill lösa utan att behöva slösa kognitiva resurser på detaljer.
- Absolut nödvändigt för alla moderna datortillämpningar.
- Orsaken till att laddningar i integrerade kretsar kan uttrycka bilden ni just nu ser och texten ni läser är en lång kedja av abstraktioner.
Dekomposition eller abstraktion?¶
Växelverkan mellan dekomposition och abstraktion¶
Vi går ner en abstraktionsnivå och implementerar get_text_from_file
¶
def calculate_word_frequencies_in_file(textfile):
tokenized_text = get_text_from_file(textfile)
return calculate_word_frequencies(tokenized_text)
def get_text_from_file(filename):
pass
if __name__ == "__main__":
# test code:
print(get_text_from_file("moby_dick_tokenized.txt")[:500])
MOBY - DICK ; or , THE WHALE . By Herman Melville CHAPTER 1 . Loomings . Call me Ishmael . Some years ago - never mind how long precisely - having little or no money in my purse , and nothing particular to interest me on shore , I thought I would sail about a little and see the watery part of the world . It is a way I have of driving off the spleen and regulating the circulation . Whenever I find myself growing grim about the mouth ; whenever it is a damp , drizzly November in my soul ; wheneve
Vi skiftar tillbaka till calculate_word_frequencies
¶
- Först måste vi implementera
get_token_list
.
def calculate_word_frequencies(tokenized_text):
token_list = get_token_list(tokenized_text)
for token in token_list:
count(token)
return frequencies
def get_token_list(tokenized_text):
pass
if __name__ == "__main__":
# test code:
test_text = "Hej hej !\nHej då . "
print(test_text)
print(get_token_list(test_text))
Hej hej ! Hej då . None
Hur ska vi göra med add_occurrence
?¶
def add_occurence(token, frequencies):
pass
Repetition: Abstrakta datatyper (ADT)¶
Exempel på abstrakt datatyp¶

- Kö (eng. queue)
- lista med element är kön, index
0
är nästa på tur - funktionen
create_empty_queue()
som returnerar en tom kö - funktionen
enqueue(value, queue)
som lägger till värdetvalue
till slutet på könqueue
- funktionen
dequeue(queue)
som returnerar värdet som är först i kön och plockar även bort det från kön
- lista med element är kön, index
Implementation av kö-ADT:n¶
def create_empty_queue():
return ('queue', [])
def enqueue(value, queue):
if queue[0] == 'queue':
queue[1].append(value)
else:
raise TypeError("Not a queue")
def dequeue(queue):
if queue[0] == 'queue':
if queue[1]:
return queue[1].pop(0)
else:
return None
else:
raise TypeError("Not a queue")
Implementation av kö-ADT:n¶
q = create_empty_queue()
print(1, q)
enqueue('a', q)
print(2, q)
enqueue('b', q)
print(3, q)
print(4, f"{dequeue(q)=}")
print(5, q)
enqueue('c', q)
print(6, q)
1 ('queue', []) 2 ('queue', ['a']) 3 ('queue', ['a', 'b']) 4 dequeue(q)='a' 5 ('queue', ['b']) 6 ('queue', ['b', 'c'])
print(7, f"{dequeue(q)=}")
print(8, f"{dequeue(q)=}")
print(9, f"{dequeue(q)=}")
7 dequeue(q)='b' 8 dequeue(q)='c' 9 dequeue(q)=None
En abstrakt datatyp för att räkna förekomster¶
def make_counter():
pass
def add_occurrence(element, counter):
pass
def get_occurrences(element, counter):
pass
if __name__ == "__main__":
# test code:
counter = make_counter()
print(f"{get_occurrences('a', counter)=}")
add_occurrence('a', counter)
print(f"{get_occurrences('a', counter)=}")
add_occurrence('a', counter)
add_occurrence('a', counter)
print(f"{get_occurrences('a', counter)=}")
print(f"{get_occurrences('b', counter)=}")
get_occurrences('a', counter)=0 get_occurrences('a', counter)=1 get_occurrences('a', counter)=3 get_occurrences('b', counter)=0
Vår räkne-ADT i calculate_word_frequencies
¶
def calculate_word_frequencies(tokenized_text):
token_list = get_token_list(tokenized_text)
for token in token_list:
count(token)
return frequencies
if __name__ == "__main__":
# test code:
test_text = "Hej hej !\nHej då . "
print(test_text)
print(calculate_word_frequencies(test_text))
Hej hej ! Hej då . {'Hej': 2, 'hej': 1, '!': 1, 'då': 1, '.': 1}
Hur kom jag på det så snabbt?¶
Jag har sett liknande problem förut.
Vi människor är experter på mönsterigenkänning, vi kan träna oss att applicera det på problemlösning.
Mönsterigenkänning¶
Har vi sett liknande problem förut?
Vad har de problemen gemensamt med vårt aktuella problem och vad har de för skillnader?
Vet vi något om hur problemet kan angripas baserat på mönstret vi känner igen?
Typer av beräkningsproblem, formellt sett¶
Beslutsproblem (eng. Decision problem)
- Finns det någon lösning?
Räkningsproblem (eng. Counting problem)
- Hur många lösningar finns det?
Sökningsproblem (eng. Search problem)
- Hitta en lösning.
Optimeringsproblem (eng. Optimization problem)
- Välj bästa möjliga lösning.
Funktionsproblem (eng. Function problem)
- Om det finns en eller flera lösningar, ge en av dem, annars svara att ingen lösning existerar.
Är alla problem någon av de 5 typerna?¶
På rätt abstraktionsnivå är väldigt många problem det, men...
I praktiken kan man hitta många andra typer av mindre abstrakta mönster som man med tiden lär sig känna igen.
- Som t.ex. en ADT för att räkna förekomster.
De faktiska mönstren lär man sig med tiden bara man lär sig att hålla utkik efter dem.
- Erfarenhet inom många områden handlar om mängden mönster man lärt sig känna igen och hur man bäst drar nytta av dem.
Hur drar vi nytta av mönsterigenkänning?¶
Om vi känner igen en typ av problem kanske vi känner till, eller åtminstone vet att det finns/inte finns, en eller flera möjliga lösningar på den typen av problem.
Sådana lösningar kallas för algoritmer.
Algoritmer, generaliseringar av processer¶
En algoritm är en metod som, givet ett väldefinierat utgångsläge, löser ett problem genom att ett ändligt antal elementära och väldefinierade operationer tillämpas i en förskriven ordning.
--- Lunell. 2011. Datorn i världen, världen i datorn
Exempel på algoritmer¶
- Informella:
- Knyta skorna
- Morgonrutin
- Recept för maträtter
- Formella:
- Addition/subtraktion/multiplikation med uppställning
- Långdivison (liggande stolen eller trappan)
- Matrismultiplikation
- Kruskals algoritm
- Binärsökning
När är en lösning en algoritm?¶
Input: Vad är indata? Vad vet vi? Vad behöver vi veta? Hur representerar vi data?
Formulera problemet: Gör problemet väldefinierat: För vilka input vill vi kunna komma fram till en lösning? Vilka är normalfallen? Finns extremfall?
Output: Hur ser en lösning ut? Vad är en korrekt lösning? Finns flera korrekta lösningar? Finns det en bästa lösning?
Formulera lösningen: Gör lösningen väldefinierad. Hur vet vi att något är en lösning?
Hela vår lösning¶
def make_counter():
return dict()
def add_occurrence(element, counter):
if element in counter.keys():
counter[element] += 1
else:
counter[element] = 1
def get_occurrences(element, counter):
if element in counter.keys():
return counter[element]
else:
return 0
def calculate_word_frequencies(tokenized_text):
token_list = get_token_list(tokenized_text)
frequencies = make_counter()
for token in token_list:
add_occurence(token.lower(), frequencies)
return frequencies
def get_token_list(tokenized_text):
return tokenized_text.split()
def calculate_word_frequencies_in_file(textfile):
tokenized_text = get_text_from_file(textfile)
return calculate_word_frequencies(tokenized_text)
if __name__ == "__main__":
# test code:
freq = calculate_word_frequencies_in_file("moby_dick_tokenized.txt")
print(f"{freq['the']=}")
print(f"{freq['whale']=}")
print(f"{freq['captain']=}")
print(f"{freq['queequeg']=}")
freq['the']=14175 freq['whale']=1152 freq['captain']=327 freq['queequeg']=252
Bara för programmering?¶
- Datalogiskt tänkande kan tillämpas i alla situationer där vi behöver skapa en mental modell över en domän.
- Detta är en färdighet som vi kan använda bland annat när vi
- gör kvalitativ analys
- skapar modeller
- designar experiment
- skriver rapporter
- Inom programmering har vi möjlighet att implementera vår lösning och testa den.
Algoritmer och komplexitet¶
Komplexitet: tid och minne¶
- En algoritms komplexitet besvarar frågan Hur förhåller sig resursanvändningen till storleken på problemet?
- Om vi skalar upp indata, hur skalar resursanvändningen upp?
- Tidskomplexitet
- Hur mycket tid (antal operationer) behöver algoritmen?
- Minneskomplexitet
- Hur mycket minne behöver algoritmen?
Komplexitet¶
- Hur mycket behöver datorn göra? Hur komplicerat är det att lösa ett givet problem med en viss algoritm?
- Exempel,
for
-loop, leta efter största elementet- Hur många operationer behöver algoritmen?
def get_max_value(values):
max_value = values[0]
for value in values:
if value > max_value:
max_value = value
return max_value
Linjär komplexitet¶
- Ju större
len(values)
är, desto mindre betydelse har antalet operationer utanförfor
-loopen.- Antal operationer är asymptotiskt proportionerligt mot
len(values)
- dubbelt så många element i
values
→ ungefär dubbelt så många operationer
- Antal operationer är asymptotiskt proportionerligt mot
- Ordo-notation (eng. Big O notation)
- För nedanstående funktion: $O(n)$, där $n$ är storleken på input
- Utläses "Ordo n" på svenska och "Order of n" på engelska.
def get_max_value(values):
max_value = values[0]
for value in values:
if value > max_value:
max_value = value
return max_value
Illustration, linjär komplexitet¶
def get_max_value(values):
print(f"{len(values)=}")
max_value = values[0]
number_of_operations = 0
for value in values:
number_of_operations +=1
if value > max_value:
max_value = value
print(f"{number_of_operations=}")
return max_value
from random import sample
random_values = sample(range(1000), k=100)
print(f"{random_values=}")
print(get_max_value(random_values))
random_values=[20, 860, 427, 672, 176, 775, 130, 921, 550, 778, 27, 450, 749, 182, 386, 592, 444, 413, 973, 306, 1, 731, 241, 30, 320, 236, 942, 374, 868, 35, 70, 404, 895, 259, 468, 646, 406, 685, 919, 307, 644, 856, 810, 515, 665, 885, 652, 477, 784, 998, 74, 313, 715, 284, 598, 863, 111, 223, 95, 489, 276, 395, 667, 442, 302, 886, 767, 394, 519, 355, 104, 412, 770, 859, 39, 524, 525, 71, 326, 850, 753, 239, 605, 606, 331, 411, 21, 904, 700, 620, 167, 800, 659, 82, 426, 762, 16, 511, 334, 267] len(values)=100 number_of_operations=100 998
Komplexitet, nästlade loopar¶
- Alla par-kombinationer man kan bilda givet en sekvens av $n$ värden
- Antag en lista
[1, 2, 3, ..., n ]
def get_pairs(values):
pairs = []
for v1 in values:
for v2 in values:
pairs.append([v1, v2])
return pairs
- $O(n^2)$ - input på $n$ värden → $\sim n^2$ operationer, kvadratisk ökning av antalet operationer
Illustration, kvadratisk komplexitet¶
def get_pairs(values):
print(f"{len(values)=}")
pairs = []
number_of_operations = 0
for v1 in values:
for v2 in values:
number_of_operations += 1
pairs.append([v1, v2])
print(f"{number_of_operations=}")
return pairs
values = range(7)
print(f"{values=}")
print(get_pairs(values))
values=range(0, 7) len(values)=7 number_of_operations=49 [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [0, 6], [1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5], [3, 6], [4, 0], [4, 1], [4, 2], [4, 3], [4, 4], [4, 5], [4, 6], [5, 0], [5, 1], [5, 2], [5, 3], [5, 4], [5, 5], [5, 6], [6, 0], [6, 1], [6, 2], [6, 3], [6, 4], [6, 5], [6, 6]]
Exempel på komplexitet i stigande ordning¶
- $O(1)$: konstant
- $O(\log n)$: logaritmisk
- $O(n)$: linjär
- $O(n \log n)$: kvasilinjär eller log-linjär
- $O(n^2)$: kvadratisk
- $O(n^3)$: kubisk
- $O(2^n)$: exponentiell
- $O(n!)$: faktoriell
Objektorientering¶
Objekt i Python¶
- Python är objektbaserat, dvs.
- Alla värden i Python är objekt , dvs. instanser av klasser.
- Många andra programmeringsspråk har några så kallade primitiva datatyper (ofta heltal, flyttal, tecken, array, etc.) som inte är objekt, Python har inga primitiva datatyper.
- I Python gäller att datatyper == klasser
Exempel: Inbyggda klassen list
¶
- Vi har klassen
list
som används (som en mall) för listor. - När vi vill skapa en specifik lista, skapar vi en instans av klassen
list
genom att anropa klassen- I fallet
list
kan vi skicka med ett argument när vi skapar en instans
- I fallet
list()
→ instans av klassenlist
som ännu inte innehåller några elementmy_variable = list()
list(range(3))
→ instans av klassenlist
som innehåller talen 0, 1, 2my_other_variable = list(range(3))
- Vi kan komma åt egenskaper (variabler) och beteenden (metoder) med punktnotation:
my_variable.reverse()
my_variable.sort()
Exempel: Inbyggda klassen list
¶
print(str)
<class 'str'>
Exempel: Inbyggda klassen list
¶
list()
[]
Exempel: Inbyggda klassen list
¶
my_variable = list()
print(my_variable)
[]
Exempel: Inbyggda klassen list
¶
print(type(my_variable))
<class 'list'>
Exempel: Inbyggda klassen list
¶
print(len(my_variable))
0
Exempel: Inbyggda klassen list
¶
print(my_variable.append(1))
None
Exempel: Inbyggda klassen list
¶
print(my_variable)
[1]
Så vad är det nya om vi jobbat med objekt hela tiden?¶
1. Inkapsling (Encapsulation)¶
- Objekt är själva ansvariga för den data de innehåller och ingen kod utanför objektet kan (givet ren objektorientering) direkt påverka det som finns inne i objektet
- Message passing: Genom att skicka meddelanden till ett objekt kan vi indirekt påverka objektet eller läsa den data som finns där i.
- Det är objektet självt som avgör vilken kod som ska köras när ett meddelande inkommer. (Dynamic dispatch)
- Ett meddelande skickas till ett objekt genom att anropa en av objektets metoder.
2. Abstraktion (Abstraction)¶
- Genom att modellera system som en samling objekt som representerar olika komponenter kan vi abstrahera bort onödig komplexitet.
- Det räcker att veta vad ett objekt gör, inte hur det gör det.
3. Arv (Inheritance) och/eller sammansättning (Composition)¶
- Arv
- Nya objekt kan skapas genom modifikationer av existerande typer utan att behöva duplicera kod.
- Mer generell kod i ett objekt och mer specifik kod i ett objekt som ärver den generella funktionaliteten från det första objektet.
- Ett objekt består alltså inte bara av en typ utan av en serie av typer med ökande abstraktionsgrad.
- Sammansättning
- Nya objekt kan skapas som innehåller instanser av andra objekt och därför kan använda deras funktionalitet utan att behöva duplicera kod.
- Kod som behövs i flera olika sammanhang kan placeras i ett objekt som inkluderas i flera olika objekt.
4. Polymorfi (Polymorphism)¶
- Objekt av olika typer kan behandlas som om de tillhörde samma typ under vissa förutsättningar.
- T.ex. objekt av olika typer som genom arv från en gemensam basklass eller prototyp delar viss mer generell funktionalitet.
Klass eller prototyp?¶
- Två olika skolor
- Klassbaserad OOP: Varje objekt tillhör en klass som beskriver vilka metoder som finns tillgängliga och vilka typer av data som objektet innehåller. (Python, Java, C++, Swift, Kotlin, Go, etc.)
- Prototypbaserad OOP: Varje objekt startar som en kopia av ett prototypobjekt som innehåller vissa metoder och data. (JavaScript, Lua, Io, AHK)
Klassbaserad objektorientering¶
- Alla objekt är instanser av en klass.
- En klass är en mall, en beskrivning av något som kan finnas i världen.
- En klass beskriver vilka egenskaper eller attribut ett objekt har och vilka beteenden det har.
- egenskaper/attribut kan jämföras med variabler
- beteenden realiseras genom metoder som kan jämföras med funktioner
- Det som finns i världen är objekt och alla objekt är instanser av klasser.
Klasser i föreläsningssalen¶
- KOGVET1?
- Proletariat? Bourgeoisie? Aristokrati?
- Nja... Snarare:
- Bänk
- Stol
- Eluttag
- Whiteboard
- Projektor
- Student
- Lärare
Egna klasser¶
- Det huvudsakliga arbetet inom objektorienterad programmering organiseras runt att designa och implementera egna klasser.
- Klassnamn bör vara substantiv och egendefinierade klasser i Python börjar alltid med stor bokstav.
- Pythons inbyggda klasser börjar dock normalt sett med liten bokstav för att särskilja dem, t.ex.
list
,dict
,int
.
- Pythons inbyggda klasser börjar dock normalt sett med liten bokstav för att särskilja dem, t.ex.
- En fil/modul kan innehålla flera klasser.
Objektorienterad version av räkne-ADT:n¶
class Counter:
def __init__(self):
self.storage = dict()
def add(self, element):
if element in self.storage.keys():
self.storage[element] += 1
else:
self.storage[element] = 1
def get_occurrences(self, element):
if element in self.storage.keys():
return self.storage[element]
else:
return 0
if __name__ == "__main__":
# test code:
c = Counter()
print(f"{c.get_occurrences('a')=}")
c.add('a')
print(f"{c.get_occurrences('a')=}")
c.add('a')
c.add('a')
print(f"{c.get_occurrences('a')=}")
print(f"{c.get_occurrences('b')=}")
print(f"{c=}")
c.get_occurrences('a')=0 c.get_occurrences('a')=1 c.get_occurrences('a')=3 c.get_occurrences('b')=0 c=<__main__.Counter object at 0x7f85bd8be080>
Klassdefinition¶
- Nyckelordet
class
används för att definiera en ny klass, på ungefär sätt somdef
används för att definiera en funktion. class
följs av namnet på den klass man skapar och kolon, ungefär som en funktionsdefinition.
class Counter:
- Inne i kodblocket för klassen, dvs i klassens kropp, defineras klassens metoder och attribut.
Metoddefinition¶
- Inne i en klasskropp defineras metoder med
def
på samma sätt som vilken funktion som helst, med ett undantag. - Första argumentet till en metod är alltid den aktuella instansen, dvs. objektet självt, och därför använder vi namnet
self
för den första parametern.- Detta är inte i strikt mening ett krav i Python, men det är en konvention man alltid ska följa.
- Inne i metoden används sedan
self
för att komma åt instansvariabler eller andra metoder.
def add(self, element):
if element in self.storage.keys():
self.storage[element] += 1
else:
self.storage[element] = 1
Magic methods i Python¶

- Python-klasser kan implementera vissa specialmetoder som inte anropas direkt utan genom andra språkliga konstruktioner, dessa kallas för magic methods
- Eftersom Python använder dubbla understreck för att indikera dessa kallas de ibland också för dundermetoder.
__init__
¶
__init__
är den vanligast förekommande magiska metoden och beskriver hur ett objekt ska skapas eller initieras.- Ofta kan
__init__
ta många olika argument men likt alla metoder är den första parameterna alltidself
. - Mer generellt kallas
__init__
för en konstruktor och motsvarande konstruktioner finns i alla språk med klassbaserad OOP. - Konstruktorn anropas inte manuellt utan genom att "anropa" själva klassen
class Counter:
...
def __init__(self):
self.storage = dict()
...
c = Counter()
__str__
¶
- Efter
__init__
är__str__
antagligen den mest vanligt förekommande dundermetoden. - Metoden
__str__
beskriver strängrepresentationen av ett objekt. __str__
anropas automatiskt av funktionenprint
eller "halvmanuellt" genom att skapa en ny sträng-instans medstr
och skicka med objektet som argument.
class Counter:
...
def __str__(self):
return f"(Counter: {self.storage})"
...
print(c)
str(c)
Icke-strukturell ADT¶
- Exempel: Person i ett matchingsprogram
- I vår abstraktion (modell) så passar personer som har ett gemensamt intresse ihop
- Vi behöver följande operationer:
- Skapa en person
p
som har namnet Adap = create_person('Ada')
- Lägg till intresse till person
p
set_interest(p, 'Bernoulli numbers')
- Ta reda på om en person
p1
och en personp2
matcharmatches(p1, p2)
- En kö är bara en datastruktur.
- Kan lagra vilken typ av data som helst.
- ADT:er kan vara mycket mer specifika än så.
Implementation av person-ADT:n¶
def create_person(name):
return {'name':name, 'interest':None}
def set_interest(person, interest):
person['interest'] = interest
def matches(person1, person2):
return person1['interest'] == person2['interest']
Implementation av person-ADT:n¶
p1 = create_person('Annie')
set_interest(p1, 'rocketry')
p2 = create_person('Adele')
set_interest(p2, 'graphical user interfaces')
p3 = create_person('Margaret')
set_interest(p3, 'rocketry')
matches(1, 3)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[57], line 1 ----> 1 matches(1, 3) Cell In[53], line 8, in matches(person1, person2) 7 def matches(person1, person2): ----> 8 return person1['interest'] == person2['interest'] TypeError: 'int' object is not subscriptable
Person-klassen¶
class Person:
def __init__(self, name):
self.name = name
self.interest = None
def set_interest(self, interest):
self.interest = interest
def matches(self, other):
return self.interest == other.interest
Person-klassen¶
p1 = Person('Annie')
p1.set_interest('rocketry')
p2 = Person('Adele')
p2.set_interest('graphical user interfaces')
p3 = Person('Margaret')
p3.set_interest('rocketry')
p3.matches(p1)
True
Arv - en kort introduktion¶
(Mer om detta i Tema 6)
DRY-principen¶
- DRY: Don't Repeat Yourself, sammanfattning av "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system."
- Kod bör skrivas på ett sådant sätt att man inte behöver skriva samma kod på flera ställen.
- Mer lättöverskådligt
- Enklare att felsöka
- Enklare att ändra
- Färre felkällor
- DRY-principen inom objektorientering har gett upphov till arv.
Arv¶
- Arv låter oss skapa härledda klasser (även kallade för subklasser).
- En härledd klass har alla egenskaper och beteenden som sin basklass (även kallad superklass), men kan utöka eller ersätta dessa.
- Analogt exempel från biologin: taxonomi över flora och fauna
Arv¶
- I många fall vill vi skapa klasser som liknar varandra och delar många egenskaper.
- Istället för att implementera samma kod flera gånger så lägger vi den gemensamma koden i en klass som sedan används som basklass för de andra klasserna.
- Exempel:
- Program som modellerar LiU med klasserna
Student
ochTeacher
- Båda dessa har t.ex. namn och liukonto, så vi skapar basklassen
Person
för att hantera detta - E-postadresser för lärare och studenter ser dock olika ut...
- Program som modellerar LiU med klasserna
Repetition: Att skapa ett objekt är att instantiera en klass¶
- Inbyggda klasser, t.ex. listor och dictionaries har "syntaktiskt socker".
a_list = []
syntaktiskt socker föra_list = list()
a_dict = {}
syntaktiskt socker föra_dict = dict()
- För egna klasser använder vi dess namn följt av parenteser (och eventuella argument). T.ex.
dog3 = Dog("Lassie")
Repetition: Punktnotation¶
- Vi använder punktnotation för att komma åt variabler och metoder för ett objekt:
objekt.variabel
objekt.metod()
- Exempel
a = [1, 2, 3]
a.append(4)
s = "hejsan"
s.upper()
class Person(object):
def __init__(self, name, pid):
self._name = name
self._pid = pid
class Student(Person):
def get_email(self):
return self._pid + "@student.liu.se"
class Teacher(Person):
def get_email(self):
return self._name.replace(' ', '.').lower() + "@liu.se"
dude1 = Teacher("Johan Falkenjack", "johsj47")
dude2 = Student("Johan Sjöholm", "johsj304")
dude1.get_email()
'johan.falkenjack@liu.se'
dude2.get_email()
'johsj304@student.liu.se'
Vad händer när en instans av en klass skapas?¶
- Python skapar ett nytt objekt (låt oss referera till den som
obj1
) som får en referens till den klass den tillhör samt vilken klass som är dess basklass. - Om metoden
__init__
är definierad iobj1
s klass, anropas den metoden med en referens tillobj1
. - Om metoden
__init__
inte är definierad iobj1
s klass, letar Python vidare iobj1
s basklass. - Om metoden
__init__
inte är definierad iobj1
s basklass, letar Python vidare iobj1
s basklass basklass - ... o.s.v. tills Python hittar en definierad
__init__
varpå den metoden körs med en referens tillobj1
Basklassen object
¶
- Arv i objektorientering beskrivs ofta som en "är en"-relation.
- En
Student
är enPerson
- En
Teacher
är enPerson
- En
- Vi kan ha hur många steg i en kedja av arv som helst, t.ex.:
- En
DepartmentHead
är enProfessor
som är enTeacher
som är enPerson
osv.
- En
- Vi har redan sagt att allting i Python är objekt. Detta är bokstavligt menat.
- I roten av trädet av alla Python-klasser, inbyggda som egenskrivna som importerade via moduler, finns klassen
object
.
Klassdefinitioner med/utan basklass¶
# definition av klassen Dog med explicit basklass; object
class Dog(object):
pass
# också godkänd definition av klassen Dog, ingen basklass angiven;
# basklassen object antas implicit av Python
class Dog:
pass
# ingen explicit basklass; Python antar att object är basklass
class GameCharacter:
pass
# explicit basklass - GameCharacter
class PlayerCharacter(GameCharacter):
pass
# explicit basklass - GameCharacter
class EnemyCharacter(GameCharacter):
pass
Klassen object
¶
- Pythons mest fundamentala basklass. Alla andra klasser ärver, i något ändligt antal steg, från
object
. - Vi har sett kod ärvd från
object
köras.- Innan vi skapade
Counter.__str__
användesobject.__str__
när vi skrev ut Counter-objekt. - Det var
object.__str__
som returnerade'<__main__.Counter object at 0x7f85bd8be080>'
- Innan vi skapade
- När vi skapade
Counter.__str__
så överskuggade vi den ärvda__str__
-metoden frånobject
Exempel: Counter med arv¶
class CounterDict(dict):
def add(self, element):
if element in self.keys():
self[element] += 1
else:
self[element] = 1
def get_occurrences(self, element):
if element in self.keys():
return self[element]
else:
return 0
Exempel: Counter med arv¶
if __name__ == "__main__":
# test code:
cd = CounterDict()
print(f"{cd.get_occurrences('a')=}")
cd.add('a')
print(f"{cd.get_occurrences('a')=}")
cd.add('a')
cd.add('a')
print(f"{cd.get_occurrences('a')=}")
print(f"{cd.get_occurrences('b')=}")
print(f"{cd=}")
cd.get_occurrences('a')=0 cd.get_occurrences('a')=1 cd.get_occurrences('a')=3 cd.get_occurrences('b')=0 cd={'a': 3}
Introduktion till klassdiagram i UML¶
UML - Unified Modelling Language¶
- Ett sätt att grafiskt representera system, t.ex. datorprogram, hur de är uppbyggda och fungerar
- UML definierar en uppsjö av olika diagram i tre grova kategorier för att beskriva olika aspekter
- Strukturdiagram, beskriver hur ett system är uppbyggt.
- Beteendediagram, beskriver hur olika processer sker i systemet.
- Interaktionsdiagram, beskriver hur en användare interagerar med systemet.
- I sin striktaste form, nästintill ett eget programmeringsspråk.
- Används dock ofta mer informellt för att illustrera specifika saker, och då utelämnas detaljer som inte behövs i det fallet
Klassdiagram¶
Klassdiagram¶
Book-klassen, definition¶
class Book:
def __init__(self, title):
self.title = title
self.author = "Unknown"
self.year = None
def print_citation(self):
if not self.year:
year = "N/A"
else:
year = self.year
print(f"{self.author}. ({year}). {self.title}")
Book-klassen, UML-diagram¶
Kodstandarder¶
- Ökad läsbarhet och lättare att orientera sig i koden
- Lättare att underhålla kod
- För Python är PEP 8, PEP 257 och PEP 287 de policy-dokument som har med kodstandard och dokumentation av kod.
- PEP står för "Python Enhancement Proposals" och är de tekniska dokument som ligger till grund för hur Python fungerar, bör användas, bör utvecklas, samt hur processerna kring detta fungerar (beslutsfattande etc.).
- Vi kommer koncentrera oss på PEP 8 och PEP 257
Krav på PEP 8 och PEP 257¶
- Från och med Temauppgift 4 kommer det att vara krav på att den kod ni skriver följer PEP 8 och PEP 257.
- För att underlätta kontroll av att er kod följer dessa kan ni antingen
- installera ett plugin till er texteditor som kontrollerar PEP 8 och PEP 257 medan ni skriver er kod, eller
- installera paket
pycodestyle
som används för att kontrollera PEP 8 och paketetpydocstyle
som används för att kontrollera PEP 257
- Både
pycodestyle
ochpydocstyle
finns installerade i en virtuell miljö som hittas i/courses/729G46/venv_pep
- aktivera med
source /courses/729G46/venv_pep/bin/activate
- kör med
pycodestyle <fil> ...
samtpydocstyle <fil> ...
- aktivera med
PEP 8 - Style Guide for Python Code¶
- Radlängd, indentering och whitespace
- Indentering: 4 mellanslag (rekommenderas)
- Radlängd: 79 tecken
- Rader bryts på rätt sätt med rekommenderad indentering
- Två tomma rader mellan funktioner
- En tom rad mellan metoder
- Namnkonventioner för variabelnamn, funktionsnamn, klassnamn
- Etc.
PEP 257 - Docstring Conventions¶
- Docstrings används för att dokumentera moduler, klasser, metoder och funktioner
- Skriv det som är viktigt för den som ska använda modulen/klassen/metoden/funktionen.
- Börjar och slutar med tre citationstecken (
"""
) - Skrivs på följande ställen
- först i en modul
- raden efter
class Klassnamn(Basklassnamn):
- raden efter
def funktionsnamn():
ochdef metodnamn():
Från PEP 257 - Docstring Conventions¶
- Källa: https://peps.python.org/pep-0257/#multi-line-docstrings
- The docstring of a script (a stand-alone program) should be usable as its “usage” message, printed when the script is invoked with incorrect or missing arguments (or perhaps with a “-h” option, for “help”). Such a docstring should document the script’s function and command line syntax, environment variables, and files.
- The docstring for a function or method should summarize its behavior and document its arguments, return value(s), side effects, exceptions raised, and restrictions on when it can be called (all if applicable).
- The docstring for a class should summarize its behavior and list the public methods and instance variables.
Utformning av en docstring¶
- Två varianter
- docstring som endast använder en rad
- docstring som använder flera rader
Utformning av en docstring på en rad¶
- En mening som slutar på punkt.
- Ska vara formulerad i imperativ (som en "order").
- Bör i princip alltid vara skriven på engelska.
- Exempel
- OK:
"""Return all odd numbers in the argument value_list."""
- Inte OK:
"""The function returns all odd numbers in the argument value_list"""
- OK:
- Fler exempel
Docstring på flera rader¶
- Sammanfattande en-radsbeskrivning som första rad; formuleras på samma sätt som en docstring på en rad.
- En tom rad, följt av resterande docstring-rader.
- Avslutande trippel-citationstecknena skrivs på en egen rad.
- Ingen tom rad mellan avslutande trippel-citationstecken och första raden kod.
- Exempel: https://peps.python.org/pep-0257/#multi-line-docstrings
Detaljnivå på docstrings¶
- En docstring är till för användare av modulen/klassen/funktionen/metoden.
- I den sammanfattande första meningen är syftet att berätta vad som görs, inte hur det görs.
- Exempel på "vad"-beskrivning (bra):
- Return a sorted list with the contents from value_list.
- Exempel på "hur"-beskrivning" (dåligt):
- Uses a temporary list to store all elements as tuples containing the element represented as a string followed by the actual element before they are sorted using a bubble sort.
Användning av olika typer av kommentarer¶
- Docstrings
- börjar och slutar med
"""
och skrivs som första rad efter bl.a. klass-/funktions-/metod-huvud - syfte: är till för att läsas av personer som vill veta hur de ska använda modulen/klassen/metoden/funktionen
- börjar och slutar med
- block-kommentarer
- kommenterar börjar med
#
och som skrivs ovanför det block som de tillhör - syfte: till för personer som utvecklar modulen/klassen/metoden/funktionen
- kommenterar börjar med
- inline-kommentarer
- kommentarer som börjar med
#
och skrivs bredvid koden som de gäller - PEP 8 säger att de ska användas sparsamt
- syfte: till för personer som utvecklar modulen/klassen/metoden/funktionen
- kommentarer som börjar med
- Se https://peps.python.org/pep-0008/#comments
Automatisk kontroll av PEP8 & PEP257¶
Pythonpaket
pycodestyle
för att kontrollera PEP8pydocstyle
för att kontroller PEP257
Tillägg för "linting" i Visual Studio Code
Mer information på kurshemsidan