729G46 Informationsteknologi och programmering¶

Tema 4, Föreläsning 4.1-4.2¶

Johan Falkenjack, johan.falkenjack@liu.se¶

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?¶

  • Läsa in data från en fil.
  • Dela upp texten i tokens, dvs enskilda ord eller skiljetecken.
  • Gå igenom alla tokens.
  • Uppdatera frekvensen för varje token.

Ett utkast¶

In [23]:
def calculate_word_frequencies_in_file(textfile):
    pass
In [ ]:
tokenized_text = get_text_from_file(textfile)
token_list = get_token_list(tokenized_text)
frequencies = dict()
for token in token_list:
    add_occurence(token, frequencies)
return frequencies

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.

Hur små delar man behöver dela upp ett verkligt problem i beror på vilka operationer som redan finns fördefinierade och vilka primitiver man har tillgång till.

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å?

    1. De som redan är implementerade av oss eller någon annan i det projekt vi arbetar.

    2. Primitiver.

Primitiver¶


  • Inbyggda komponenter i det programmeringsspråk vi använder.

  • Vilka primitiver finns i Python?
  • Tilldelning och variabler
  • Loopkonstruktioner
  • Aritmetiska och logiska operatorer
  • Inbyggda funktioner
  • Sätt att definiera funktioner och klasser

Tillbaka till vårt utkast¶

In [23]:
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¶

In [24]:
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 i moby_dick_tokenized.txt, hur get_text_from_file åstadkommer det behöver vi inte fundera på.
  • 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¶

In [31]:
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
infile = open(filename)
    text = infile.read()
    infile.close()
    return text
  • Eftersom vi abstraherat calculate_word_frequencies kan vi fokusera på get_text_from_file.
  • Vi kan testa funktionen oberoende av annat och se att den verkar fungera.

Vi skiftar tillbaka till calculate_word_frequencies¶

  • Först måste vi implementera get_token_list.
In [35]:
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
In [47]:
def get_token_list(tokenized_text):
    return tokenized_text.split()

Hur ska vi göra med add_occurrence?¶

In [ ]:
def add_occurence(token, frequencies):
    pass
  • Vad ska vi göra om token är ett ord vi aldrig stött på förut?
  • Vad ska vi göra om token är ett ord vi HAR stött på förut?
  • Detta är ett vanligt problem.

Repetition: Abstrakta datatyper (ADT)¶

Exempel på abstrakt datatyp¶

No description has been provided for this image
  • 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ärdet value till slutet på kön queue
    • funktionen dequeue(queue) som returnerar värdet som är först i kön och plockar även bort det från kön

Implementation av kö-ADT:n¶

In [46]:
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¶

In [47]:
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'])
In [48]:
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¶

In [39]:
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
In [40]:
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
    
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¶

In [48]:
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}
In [ ]:
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

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.
  • Kan ni komma på något exempel på någon av dessa problemtyper som vi stött på i kursen?

Ä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
  • datavetare + autism = fler vardagsalgoritmer
  • ADHD → svårt att följa dem

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¶

In [53]:
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ör for-loopen.
    • Antal operationer är asymptotiskt proportionerligt mot len(values)
    • dubbelt så många element i values → ungefär dubbelt så många operationer
  • 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¶

In [13]:
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
In [14]:
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¶

In [16]:
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
In [17]:
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
time_complexity.webp

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
  • list() → instans av klassen list som ännu inte innehåller några element
    • my_variable = list()
  • list(range(3)) → instans av klassen list som innehåller talen 0, 1, 2
    • my_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¶

In [39]:
print(str)
<class 'str'>

Exempel: Inbyggda klassen list¶

In [40]:
list() 
Out[40]:
[]
  • Detta är vad som egentligen händer när vi initierar en variabel med tomma listan.

Exempel: Inbyggda klassen list¶

In [41]:
my_variable = list()
print(my_variable)
[]

Exempel: Inbyggda klassen list¶

In [42]:
print(type(my_variable))
<class 'list'>

Exempel: Inbyggda klassen list¶

In [43]:
print(len(my_variable))
0

Exempel: Inbyggda klassen list¶

In [44]:
print(my_variable.append(1)) 
None

Exempel: Inbyggda klassen list¶

In [45]:
print(my_variable)
[1]

Så vad är det nya om vi jobbat med objekt hela tiden?¶

  • Vi höjer blicken.
  • Vi bygger våra egna klasser.

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 OOP är idag totalt dominerande, med det enda undantaget det absolut vanligaste programmeringsspråket på planeten.
  • Varför pratar jag om andra programmeringsspråk hela tiden?

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.
  • En fil/modul kan innehålla flera klasser.

Objektorienterad version av räkne-ADT:n¶

In [59]:
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>
In [ ]:
    def __str__(self):
        return f"(Counter: {self.storage})"

Klassdefinition¶

  • Nyckelordet class används för att definiera en ny klass, på ungefär sätt som def 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¶

No description has been provided for this image
  • 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 alltid self.
  • 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 funktionen print eller "halvmanuellt" genom att skapa en ny sträng-instans med str 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 Ada
    • p = create_person('Ada')
  • Lägg till intresse till person p
    • set_interest(p, 'Bernoulli numbers')
  • Ta reda på om en person p1 och en person p2 matchar
    • matches(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¶

In [53]:
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¶

In [54]:
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')
In [57]:
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
  • Annie Easley, en av NASAs första svarta anställda, utbildade sig till matematiker och programmerare innan "programmerare" ens var ett etablerat begrepp. Genomförde många av de grundläggande beräkningarna för NASAs raketer.
  • Adele Goldberg, objektorienteringspionjär och var en av drivkrafterna bakom Smalltalk-80, ett av de första objektorienterade programmeringsspråken och hennes arbete ligger till grund för de fönstersystem som används i de flesta moderna operativsystemen.
  • Margaret Hamilton, programmeringschef för Apollo-landaren på NASA.

Person-klassen¶

In [58]:
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¶

In [59]:
p1 = Person('Annie')
p1.set_interest('rocketry')
p2 = Person('Adele')
p2.set_interest('graphical user interfaces')
p3 = Person('Margaret')
p3.set_interest('rocketry')
In [63]:
p3.matches(p1)
Out[63]:
True
  • Annie Easley, en av NASAs första svarta anställda, utbildade sig till matematiker och programmerare innan "programmerare" ens var ett etablerat begrepp. Genomförde många av de grundläggande beräkningarna för NASAs raketer.
  • Adele Goldberg, objektorienteringspionjär och var en av drivkrafterna bakom Smalltalk-80, ett av de första objektorienterade programmeringsspråken och hennes arbete ligger till grund för de fönstersystem som används i de flesta moderna operativsystemen.
  • Margaret Hamilton, programmeringschef för Apollo-landaren på NASA.

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 och Teacher
    • 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...

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ör a_list = list()
    • a_dict = {} syntaktiskt socker för a_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()
In [14]:
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")
In [15]:
dude1.get_email()
Out[15]:
'johan.falkenjack@liu.se'
In [16]:
dude2.get_email()
Out[16]:
'johsj304@student.liu.se'

Vad händer när en instans av en klass skapas?¶

  1. 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.
  2. Om metoden __init__ är definierad i obj1s klass, anropas den metoden med en referens till obj1.
  3. Om metoden __init__ inte är definierad i obj1s klass, letar Python vidare i obj1s basklass.
  4. Om metoden __init__ inte är definierad i obj1s basklass, letar Python vidare i obj1s basklass basklass
  5. ... o.s.v. tills Python hittar en definierad __init__ varpå den metoden körs med en referens till obj1

Basklassen object¶

  • Arv i objektorientering beskrivs ofta som en "är en"-relation.
    • En Student är en Person
    • En Teacher är en Person
  • Vi kan ha hur många steg i en kedja av arv som helst, t.ex.:
    • En DepartmentHead är en Professor som är en Teacher som är en Person osv.
  • 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ändes object.__str__ när vi skrev ut Counter-objekt.
    • Det var object.__str__ som returnerade '<__main__.Counter object at 0x7f85bd8be080>'
  • När vi skapade Counter.__str__ så överskuggade vi den ärvda __str__-metoden från object

Exempel: Counter med arv¶

In [60]:
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
  • Bara för att man kan göra något så betyder det inte att man ska.

Exempel: Counter med arv¶

In [62]:
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 beskriver klasser och deras relationer till varandra.
  • En typ av strukturdiagram på relativt låg abstraktionsnivå.
  • En av de vanligaste typerna av UML-diagram att stöta på.
  • En enskild klass i ett klassdiagram ritas som till höger.

Klassdiagram¶

  • Ofta anger vi datatyper där det är praktiskt.

Book-klassen, definition¶

In [ ]:
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¶

  • Här anger vi datatyp även i konstruktorns parameterlista

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
    • https://www.python.org/dev/peps/pep-0008/
    • https://www.python.org/dev/peps/pep-0257/

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 paketet pydocstyle som används för att kontrollera PEP 257
  • Både pycodestyle och pydocstyle 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> ... samt pydocstyle <fil> ...

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(): och def 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"""
  • Fler exempel
    • https://peps.python.org/pep-0257/#one-line-docstrings

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
  • 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
  • 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
  • Se https://peps.python.org/pep-0008/#comments

Automatisk kontroll av PEP8 & PEP257¶

  • Pythonpaket

    • pycodestyle för att kontrollera PEP8
    • pydocstyle för att kontroller PEP257
  • Tillägg för "linting" i Visual Studio Code

  • Mer information på kurshemsidan

    • https://www.ida.liu.se/~729G46/tips/pep/kontrollera_pep/