52
729G46 Informationsteknologi och programmering Tema 4. Föreläsning 2 Jody Foo

729G46 Informationsteknologi och programmering729G46/kursmaterial/... · 2020. 11. 3. · Objektorienterad programmering (OOP) ⁃ Objekt är i fokus jämfört med funktioner i fokus

  • Upload
    others

  • View
    7

  • Download
    0

Embed Size (px)

Citation preview

  • 729G46 Informationsteknologi och programmeringTema 4. Föreläsning 2

    Jody Foo

  • Föreläsningsöversikt⁃ Forts. Objektorienterad programmering & syntax i Python ⁃ Mutable, immutable-objekt. Variabler som referenser ⁃ Programmeringsmetod

    Arbetsflöde: implementation, testning, felsökning

    Använd interaktiva skalet för testning!

    Kodstandarder: PEP8 & PEP257

    ⁃ Uppdelning av program i funktioner

  • Objektorienterad programmering (OOP)⁃ Objekt är i fokus jämfört med funktioner i fokus som vid

    funktionell programmering.

    ⁃ Man försöker relatera data och metoder till objekt. ⁃ Både data och metoder kan "ägas"/tillhöra objekt

    (inkapsling)

    ⁃ Objekt, dess egenskaper och beteenden (vad objektet kan göra och vad man kan göra med objektet).

  • Terminologi⁃ Klass ⁃ Objekt / Instans (synonymer) ⁃ Instansvariabel ⁃ Metod

    ⁃ Anm: Olika programmeringsspråk kan tyvärr använda olika termer för samma begrepp och översätts till svenska inkonsekvent (attribut/egenskap). De ovanstående kan i princip ses som standardiserade.

  • Klasser jmf med objekt⁃ En klass är en mall, en beskrivning av något som kan

    finnas i världen.

    ⁃ En klass beskriver vilka egenskaper (variabler) klassen har och vilka beteenden (metoder) den har.

    ⁃ Det som finns i världen är instanser av klasser, även kallade för objekt.

  • Punktnotation⁃ (modul.)KlassNamn ⁃ (modul.)funktion()

    ⁃ objekt.metod() ⁃ objekt.instansvariabel

    ⁃ a = [1, 2, 3] ⁃ a.append(4)

    ⁃ s = "hejsan" ⁃ s.upper()

  • Hur skapar vi klasser och objekt?

  • Klassen, dess egenskaper och beteenden

    namn

    egenskaper

    beteenden

  • Klassen, dess egenskaper och beteenden

    klassens namn

    instansvariabler

    metoder

  • Klassdiagram i UML Unified Modelling Language

    Klassnamn

    namn:attributtypnamn:attributtyp=defaultvärde

    operation()operation():returdatatypoperation(parameterlista)operation(parameterlista):returdatatyp

  • Definition av en klass

    classDog(object):

    def__init__(self,namev):self.name=namevself.age=0

    deftalk(self):print("Voff,jagheter"+self.name)

  • Skapa ett objekt: instantiera en klass⁃ Inbyggda klasser, t.ex. listor och dictionaries har syntaktiskt

    socker.

    ⁃ För egna klasser använder vi dess namn följt av parenteser (och eventuella argument). T.ex. dog = Dog("Fido")

  • Vi skapar några hundar

    classDog(object):

    def__init__(self,namev):self.name=namevself.age=0

    deftalk(self):print("Voff,jagheter"+self.name)

    hund1=Dog("Pluto")hund2=Dog("Lassie")

    Hund

    name: "Pluto" age: 0

    talk

    Hund

    name: "Lassie" age: 0

    talk

    hund1

    hund2

  • Testar hundarnaclassDog(object):

    def__init__(self,namev):self.name=namevself.age=0

    deftalk(self):print("Voff,jagheter"+self.name)

    hund1=Dog("Pluto")hund2=Dog("Lassie")

    print(hund1.age)print(hund1.name)hund1.talk()

    print(hund2.age)print(hund2.name)hund2.talk()

  • Ändra värden

    hund1.age=4hund2.age=8

    Hund

    name: "Pluto" age: 4

    talk

    Hund

    name: "Lassie" age: 8

    talk

    hund1

    hund2

  • Definiera klasser, skapa objekt

  • Definiera en klass⁃ Python för bok över vilka klasser som finns. Varje klass lagras

    i en hierarki av klasser.

    ⁃ Klasshierarkin finns för att vi ska kunna återanvända kod från "klassföräldrar", basklasser (kallas ibland även för superklasser).

    ⁃ För att definiera en klass använder vi nyckelordet class följt av ett namn, samt ett parentespar med den nya klassens basklass. Tills vidare låter vi denna vara klassen object.

    ⁃ Definition av ny klass, Book. class Book(object): 
 pass

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

  • Ett till exempel

  • Klassen Book

    print_citation()

    title : strauthor : str = "Unknown"year : int = None

    Book

  • Definition av en klassclassBook(object):

    def__init__(self,title):self.title=titleself.author="Unknown"self.year=None

    defprint_citation(self):citation="{}.({}).{}."ifnotself.year:year="N/A"else:year=self.yearprint(citation.format(self.author,year,self.title))

  • Definition av en klassclassBook(object):

    def__init__(self,title):self.title=titleself.author="Unknown"self.year=None

    defprint_citation(self):citation="{}.({}).{}."ifnotself.year:year="N/A"else:year=self.yearprint(citation.format(self.author,year,self.title))

    Nyckelordet class berättar att vi definierar en klass.

    class Book(object):

    betyder alltså "vi ska nu definiera klassen Book som har object som basklass"

    Klassnamn bör vara substantiv och ska enligt standard börja med stor bokstav.

    Efter klassnamnet kommer namnet på klassens basklass

  • Definition av en klassclassBook(object):

    def__init__(self,title):self.title=titleself.author="Unknown"self.year=None

    defprint_citation(self):citation="{}.({}).{}."ifnotself.year:year="N/A"else:year=self.yearprint(citation.format(self.author,year,self.title))

    Nyckelordet "def" används för att definiera metoder i en klass.

    Variabler som hör till en instans har "self." som prefix.

    Alla metoder tar in en referens till den instans som de tillhör som första argument. Python skickar detta automatiskt.

  • Definition av en klassclassBook(object):

    def__init__(self,title):self.title=titleself.author="Unknown"self.year=None

    defprint_citation(self):citation="{}.({}).{}."ifnotself.year:year="N/A"else:year=self.yearprint(citation.format(self.author,year,self.title))

    Metoden __init__ är speciell. Den beskriver det som händer när en instans av klassen skapas.

    Instansvariabler, variabler som tillhör en instans av en klass används med referensen self.

    self är en referens till den instans metoden anropas ifrån. Python skickar med den automatiskt när ett anrop på formen objekt.metod() görs.

  • Vi skapar böckerclassBook(object):

    def__init__(self,title):self.title=titleself.author="Unknown"self.year=None

    defprint_citation(self):citation="{}.({}).{}."ifnotself.year:year="N/A"else:year=self.yearprint(citation.format(self.author,year,self.title))

    book1=Book("ThePracticeofComputingusingPython")book2=Book("PSwAaDSuP")

    Book

    title: "The Practice of Computing Using Python" author: "Unknown" year: "N/A"

    print_citation

    book1

    book2Book

    title: "PSwAaDSuP" author: "Unknown" year: "N/A"

    print_citation

  • Instansvariabler och metodanropclassBook(object):

    def__init__(self,title):self.title=titleself.author="Unknown"self.year=None

    defprint_citation(self):citation="{}.({}).{}."ifnotself.year:year="N/A"else:year=self.yearprint(citation.format(self.author,year,self.title))

    book1=Book("ThePracticeofComputingusingPython")book2=Book("PSwAaDSuP")

    print(book1.title)print(book2.author)book1.print_citation()

    print(book2.title)print(book2.author)book2.print_citation()

    book1.author="Punch,W.F.,&Enbody,R"book1.year="2017"book1.print_citation()

  • Variabler som referenser⁃ De flesta datatyper är förändringsbara, eng. mutable.

    ⁃ För förändringsbara datatyper fungerar variabler som referenser.

    ⁃ Flera variabler kan referera till samma värde.

    ⁃ Om man ändrar värdet återspeglas detta hos alla refererande variabler.

    ⁃ Exempel: a = ["hej"]

    b = a

    a.append("hopp")

    b.append("ojdå")

    print(a)→ ["hej", "hopp", "ojdå"] print(b)→ ["hej", "hopp", "ojdå"]

  • Variabler refererar till instanser av klasserclassBook(object):...

    book1=Book("Titel1")book2=book1

    book1.title="Pythonfrånbörjan"print(book2.title)

  • ProgrammeringsmetodImplementation, testning, felsökning

  • Konsten att skriva bättre kod1. kommentera

    2. skriv kod

    3. testa

    4. rätta buggar

    5. omfaktorera

  • PEP 8⁃ Standard för att skriva pythonkod: PEP 8

    krav på att följa PEP 8 fr.o.m. Temauppgift 4

    https://www.python.org/dev/peps/pep-0008/

    ⁃ Exempel Indentering: 4 mellanslag (rekommenderas)

    Radlängd: 79 tecken

    Rader bryts på rätt sätt med rekommenderad indentering

    Användning av tomma rader, mellanrum mellan operatorer etc.

    Variabelnamn, funktionsnamn, klassnamn

  • Kontrollera att koden följer PEP8⁃ Installerade paket för kontroller i virtuella miljön i

    kurskatalogen

    $ source /courses/729G46/venv/bin/activate

    $ pycodestyle

  • PEP 257⁃ Standard för att skriva pythonkommentarer: PEP257

    krav på att följa från och med Temauppgift 5

    https://www.python.org/dev/peps/pep-0257/

    ⁃ "docstrings", för användaren - den som ska använda koden Första raden i en modul/klass/funktion/metod

    Börja och sluta med trippel-citat, """

    ⁃ Löpande kommentarer för utvecklaren (er själva), som #-kommentarer i koden. Utvecklarkommentarer till kod läggs ovanför aktuell kod.

    ⁃ Olika detaljnivå på funktionskommentarer och löpande kommentarer

  • PEP 257⁃ En docstrings första rad ska vara en mening som beskriver

    modulen/funktionen/klassen/metoden.

    ⁃ Om en funktion eller metod beskrivs, ska meningen stå i imperativ: Ja: "Returnera summan av value1 och value2."

    Ja: "Returnera det största värdet i listan values."

    Nej: "Funktionen returnerar summan av value1 och value2."

    ⁃ Om förtydligande behövs, kan detta skrivas på flera efterföljande rader.

    ⁃ Skriv det som är viktigt för den som ska använda funktionen/metoden.

  • Kontrollera att koden följer PEP257⁃ Installerade paket för kontroller i virtuella miljön i

    kurskatalogen

    $ source /courses/729G46/venv/bin/activate

    $ pydocstyle

  • Att testa sin kod⁃ Testdriven programmering - utanför omfånget för denna kurs

    ⁃ Vi använder spårutskrifter och pythons interaktiva läge.

    ⁃ Vad borde funktionen/metoden returnera? Gör den det?

    ⁃ Interaktivt läge: ipython3 -i

    ⁃ Kommandon: ls, cd, cat etc fungerar

    %run - kör fil

    %reset - rensa minnet från användarens variabler

    ⁃ Ctrl-D för att avsluta (Ctrl-D är även kontrolltecknet för EOT, End of Transmission)

  • Använda python interaktivt⁃ Anropa funktioner, skapa instanser av klasser från fil(er)

    som laddats in i minnet.

    ⁃ Allt man kan göra från en textfil.

    ⁃ Kom dock ihåg att allt ligger kvar i minnet: globala variabler

    moduler som laddats in

  • Icke-interaktiv testning av koden⁃ Vi kan skriva anrop som testar vår kod i samma fil som

    koden.

    ⁃ Bra: organisera koden så att du hittar

    ⁃ Rekommendation: samla alla testanrop längst ner i filen. Inte direkt efter funktionsdefinitionen.

    ⁃ Rekommendation: skydda testanropen bakom en if __name__ == "__main__":

    # testkod här

    Detta för att undvika att testkoden körs när man använder koden som en modul.

  • Rätta buggar⁃ syntaxfel

    ⁃ logiska fel

    ⁃ runtime-fel (inträffar under körning)

  • Omfaktorering⁃ Skriva om koden så att den blir mer lättläst och/eller

    lättare att underhålla utan att ändra på det koden gör

    ⁃ När? När koden "luktar illa", eng. "code smells"

    ⁃ T.ex. för långa funktioner

    onödigt komplicerad kod

    dåliga variabel-/funktions-/metod-/klassnamn (för långa/korta/intetsägande/missvisande)

    upprepad kod

    m.m.

  • Omfaktorering, exempel⁃ Dela upp långa funktioner i mindre funktioner

    ⁃ Generalisera funktioner

    ⁃ Förbättra variabelnamn

    ⁃ Ändra på datastrukturer

  • Uppdelning av problem

  • Programflöde och uppdelning av problem⁃ Testning blir lättare om vi delar upp koden i funktioner

    eller motsvarande för klasser, delar upp beteenden i olika metoder.

    ⁃ Se till att funktioner/metoder har en uppgift.

    ⁃ Olika principer för programmering SOLID, objektorienterad programmering

    DRY, "Don't repeat yourself"

  • Programflöde och uppdelning av problem⁃ Exempel på tekniker

    utbrytning av funktion(er): lyfta ut delar av koden från en funktion och stoppa in den i en funktion (→ DRY)

    omfaktorering av funktion/metod: ändra hur en funktion/metod är implementerad utan att ändra dess signatur (dess input och output)

    generalisering: gör det möjligt att styra vad en funktion gör med hjälp av argument, kan göras utan att ändra på funktionens signatur i bl.a. Python

  • Dela upp funktioner/metoder⁃ Målet är att en funktion/metod gör en sak. Varför?

    det är lättare att köra flera funktioner/metoder i sekvens än att uttrycka "kör allt i funktionen X förrutom ..."

    ⁃ Man kan alltid anropa på andra funktioner/metoder från en annan funktion/metod

  • Exempel på utbrytning av del av funktion 1defmain():forfilenameinsys.argv[1:]:open_file=open(filename)data=[]forlineinopen_file:#enradblirenlistadärvarjekomma-separerat#värdeblirettelementdata.append(line.rstrip().split(","))

    open_file.close()

    #allasannapositivatp_rows=[]forrowindata:ifrow[3]=="[n]"androw[4]=="[n]":fp_rows.append(row)

    #allasannanegativatn_rows=[]forrowindata:ifrow[3]=="[b]"androw[4]=="[b]":tn_rows.append(row)

    #allafalskapositivafp_rows=[]forrowindata:ifrow[3]=="[b]"androw[4]=="[n]":fp_rows.append(row)

    #allafalskanegativafn_rows=[]forrowindata:ifrow[3]=="[n]"androw[4]=="[b]":fn_rows.append(row)

  • Exempel på utbrytning av del av funktion 1defload_data(filename):"""ReturnerainnehålliCSV-filsomnästladlista.Enradblirenlistadärvarjekomma-separeratvärdeblirettelement."""open_file=open(filename)data=[]forlineinopen_file:data.append(line.rstrip().split(","))open_file.close()returndata

    defmain():forfilenameinsys.argv[1:]:data=load_data(filename)

    #allasannapositivatp_rows=[]forrowindata:ifrow[3]=="[n]"androw[4]=="[n]":fp_rows.append(row)

    #allasannanegativatn_rows=[]forrowindata:ifrow[3]=="[b]"androw[4]=="[b]":tn_rows.append(row)

    #allafalskapositivafp_rows=[]forrowindata:ifrow[3]=="[b]"androw[4]=="[n]":fp_rows.append(row)

    #allafalskanegativafn_rows=[]forrowindata:ifrow[3]=="[n]"androw[4]=="[b]":fn_rows.append(row)

  • Exempel på utbrytning av del av funktion 1defload_data(filename):"""ReturnerainnehålliCSV-filsomnästladlista.Enradblirenlistadärvarjekomma-separeratvärdeblirettelement."""open_file=open(filename)data=[]forlineinopen_file:data.append(line.rstrip().split(","))open_file.close()returndata

    deffilter(index1,index2,value1,value2,value_list):"""Returneralistamedelementfrånvalue_listsomuppfyllerspec.kriterier.

    value_list[index1]måstevaravalue1ochvalue_list[index2]måstevaravalue2."""filtered=[]forvalueinvalue_list:ifvalue_list[index1]==value1andvalue_list[index2]==value2:filtered.append(value)returnfiltered

    defmain():forfilenameinsys.argv[1:]:data=load_data(filename)

    #allasannapositivatp_rows=filter(3,4,"[n]","[n]")#allasannanegativatn_rows=filter(3,4,"[b]","[b]")#allafalskapositivafp_rows=filter(3,4,"[b]","[n]")#allafalskanegativafn_rows=filter(3,4,"[n]","[b]")

  • Exempel på utbrytning av del av funktion 2defmain():forfilenameinsys.argv[1:]:open_file=open(filename)data=[]forlineinopen_file:#enradblirenlistadärvarjekomma-separerat#värdeblirettelementdata.append(line.rstrip().split(","))

    open_file.close()

    #allasannapositivatp_rows=[]tn_rows=[]fp_rows=[]fn_rows=[]forrowindata:ifrow[3]=="[n]"androw[4]=="[n]":fp_rows.append(row)elifrow[3]=="[b]"androw[4]=="[b]":tn_rows.append(row)elifrow[3]=="[b]"androw[4]=="[n]":fp_rows.append(row)elifrow[3]=="[n]"androw[4]=="[b]":fn_rows.append(row)

  • Exempel på utbrytning av del av funktion 2defcategorize_data(data)catagorized={"tp":[],"tn":[],"fp":[],"fn":[]}forrowindata:ifrow[3]=="[n]"androw[4]=="[n]":categorized["tp"].append(row)elifrow[3]=="[b]"androw[4]=="[b]":categorized["tn"].append(row)elifrow[3]=="[b]"androw[4]=="[n]":categorized["fp"].append(row)elifrow[3]=="[n]"androw[4]=="[b]":categorized["fn"].append(row)returncategorized

    defload_data(filename):open_file=open(filename)data=[]forlineinopen_file:#enradblirenlistadärvarjekomma-separerat#värdeblirettelementdata.append(line.rstrip().split(","))open_file.close()returndata

    defmain():forfilenameinsys.argv[1:]:data=load_data(filename)categorized_data=categorize_data(data)

  • Börja uppifrån, precis som en HTA...⁃ Dela upp problem:

    Vad är det övergripande problemet?

    Vilka delsteg finns?

    Finns det återkommande moment?

    Kan man skriva en funktion som utför det återkommande momentet?

    ⁃ Använd "Dela upp problem" på varje delsteg