Föreläsning 6
Objektorienterad programmering
Dagens agenda
2
Återkoppling från kursnämndsmötet
3
Objektorienterad programmering
4
Objektorienterad programmering
Objektorienterad programmering är ett sätt att samla data (variabler) med funktioner som hör till datan i en klass.
5
En klass kan ses som en ritning eller mall för det den beskriver. Den data som hör ihop med en klass kallar vi för attribut och de funktioner som hör ihop med klasserna kallar vi för metoder.
Attributen är alltså någon sorts egenskap som klassen har, medan metoderna är saker som klassen kan göra eller saker vi kan göra med vår objekt.
Klasser & Objekt
6
Snigelobjekt1
class Pepparkakssnigel
Skapar snigelobjekt
Snigelobjekt2
Snigelobjekt3
Klasser & Objekt
7
class Snigel
mario.hoppa(1)
Låt snigeln ”mario” hoppa ett skutt genom att kalla på metoden hoppa med parametern 1.
Exempel på metoder som du redan sett
8
Klass | Exempel på objekt | Metodanrop |
list | lista = [23, 54, 12] | lista.sort() |
dictionary | dict = {‘katt’: ‘cat’, ‘hund’ : ‘dog’} | dict.keys() |
Skapa egna klasser
9
Att bygga en egen klass
Vi bestämmer vilka egenskaper objekten ska ha och vad som kan göras med dem med hjälp av metoderna i klassen. På så sätt kan vi bygga våra egna datastrukturer.
10
class Spelare:
�def __init__(self, namnet, poängen):
self.namn = namnet
self.poäng = poängen
def mer_poäng(self):
self.poäng += 1
Vilka egenskaper ska objekten ha?
Vad ska vi kunna göra med objekten?
Att skapa objekt med attribut
Vi kan skapa objekt genom att skriva klassens namn och skicka med värdena på de attribut (egenskaper) som behövs som parametrar. Tänk på att spara objektet på någon sätt, en variabel eller i en lista är ofta lämpligt.
11
spelare1 = Spelare(”Lille-skutt", 23)
spelare2 = Spelare(”Skal-man", 25)
Vi kommer åt värdena i attributen genom objekten:
spelare1.namn
spelare2.poäng
Konstruktorn: __init__
Det är i konstruktorn (__init__) som vi initierar värden på attributen, den anropas automatiskt när vi skapar objekt:
12
spelare1 = Spelare(”Lille-skutt", 23)
spelare2 = Spelare(”Skal-man", 25)
class Spelare:
�def __init__(self, namnet, poängen):
self.namn = namnet
self.poäng = poängen
När vi skriver så här anropas __init__ (utan att vi explicit behöver skriva __init__())
Metoder
Alla metoder i klassen måste ha self som parameter. self står för objektet själv, det som metoden anropas på och skickas därför inte med i anropet.
13
class Spelare:
�def __init__(self, namnet, poängen):
self.namn = namnet
self.poäng = poängen
def mer_poäng(self):
self.poäng += 1
spelare1 = Spelare(”Lille-skutt", 23)
spelare1.mer_poäng()
Nu kommer Lille-skutts poäng uppdateras med +1
Att skriva ut objekt
14
class Spelare:
�def __init__(self, namnet, poängen):
self.namn = namnet
self.poäng = poängen
def mer_poäng(self):
self.poäng += 1
spelare1 = Spelare(”Lille-skutt", 23)
print(spelare1)
Det här kommer bli lite otydligt…
Vi behöver en __str__ metod!
15
class Spelare:
�def __init__(self, namnet, poängen):
self.namn = namnet
self.poäng = poängen
def __str__ (self):
return self.namn + " fick " + str(self.poäng) + " poäng"
def mer_poäng(self):
self.poäng += 1
__str__ talar om hur objektet ska se ut när det skrivs ut.
__str__ MÅSTE returnera en sträng. Det är en fördefinerad metod som ska bete sig på samma sätt i alla klasser.
Fördefinierade metoder: __init__ & __str__
16
Dessa anropas automatiskt när vi agerar på olika sätt. �
Alla klasser bör ha dessa två metoder (__str__ kan dock bytas ut mot __repr__) med undantag från abstrakta klasser som bara samlar funktioner men där vi inte tänkt skapa objekt. (Vi kommer dock troligtvis inte ha några abstrakta klasser i den här kursen).�
Polymorfism
17
Med polymorfism menas här möjligheten att ha en metod med samma namn i olika klasser och få olika resultat.
Exempel: metoden __str__
print(objekt)
skriver ut olika saker beroende på objektets klass
Jättemånga klasser har en __str__-metod men de skriver nästan alltid ut olika saker!
En klass med kontakterna i telefonboken!
18
class Kontakt:
"""En klass som representerar en kontakt i en telefonbok"""
def __init__(self, namn, tel, pnr):
self.namn = namn
self.telefonnummer = tel
self.pnr = pnr
�def __str__(self):
"""Returnerar en sträng som är redo att skrivas in i textfilen."""
return self.namn + ', ' + self.telefonnummer + ', ' + self.pnr + '\n'
Observera: vi behöver inte döpa attributen till samma sak som parametrarna!
Attributet heter telefonnummer men namnet på parametern som vi lägger in när vi skapar objekt kallas för tel.
Vilka attribut har Kontakt?
Namnge klasser
19
Komihåg: vi kallar på både funktioner och klasser genom att skriva deras namn följt av en parentes!
Vi skapade flera sniglar, men varje snigel var av klassen snigel()
Samma som för funktioner!
Funktioner
Funktioner är definierade utanför klassen och generella.
T.ex. vill vi ha funktioner för felhantering, menyn, inläsning från fil, skapande av objekt och för vårt huvudprogram.
Metoder
Metoder är definierade inne i klassen och specifika för klassen och dess objekt.
En grundregel är ett en metod bör använda objektets attribut på något sätt.
20
vs.
Skapa egna metoder
21
Vad kommer skrivas ut?
Använd en menyfunktion
22
I många av våra program vill vi ha en meny, men den bör vara utanför klassen.
Funktioner i menyn kommer dock troligtvis använda metoder i klassen.
Varför är klasser bra att ha?
23
Klasser är ett sätt att strukturera kod. De gör det lättare att återanvända kod och tillåter oss att skapa egna datastruktuer vilket gör det lättare att hålla koll på våra värden.
Vissa språk är byggda kring idéen om klasser, de kallas för objektorienterade. Java är ett sådant språk.
Python uppmuntrar till att använda klasser, men som ni märkt går det bra att programmera i python utan att använda klasser.
Att ha flera objekt
24
Många objekt
25
Klasser är väldigt bra om vi vill skapa många objekt (vilket vi ofta vill göra).
Tidigare har vi använt matriser (listor i listor) eller dictionaries med värden som är listor, för att kunna lagra flera värden, men nu kan vi istället lagra objekt i listor. Det blir då lättare att hålla koll på vilka värden som finns, eftersom vi har attribut istället där vi kan lagra olika saker.
Spara flera objekt
26
Låt oss titta på våra kontaktobjekt igen!
Observera: vi måste spara objekten någon stans! I detta fall i ett dictionary.
Skriva ut objekt i en lista eller dictionary
27
class Tal():
def __init__(self, tal):
self.tal = tal
def __str__(self):
return "från str: " + str(self.tal)
def __repr__(self):
return "från repr: " + str(self.tal)
def main():
lista=[]
dict = {}
for i in range(5):
objekt = Tal(i)
lista.append(objekt)
dict[i] = objekt
�print('lista', lista)
print('dict', dict)
�print('Element i lista:')
for element in lista:
print(element)
print('\nElement i dictionary:')
for nyckel in dict.keys():
print(dict[nyckel])
�main()
Kolla vilken strängmetod som används för vilken utskrift!
Läsa från och spara på fil
Precis som vi sparat data på fil och läst från fil tidigare kan vi vilja göra detta även med data som vi lagrar i objekt.
28
def skapa_telefonbok(filnamn):
telefonbok = {}
with open(filnamn, 'r') as fil:
for rad in fil:
kontakt = rad.split(',')
namn = kontakt[0].strip()
tel = kontakt[1].strip()
pnr = kontakt[2].strip()
kontakt = Kontakt(namn, tel, pnr)
telefonbok[namn] = kontakt
return telefonbok
def spara_telefonbok(filnamn, telefonbok):
with open(filnamn, 'w') as fil:
for nyckel in telefonbok.keys():
fil.write(str(telefonbok[nyckel]))
Jämföra objekt
29
För att kunna sortera våra objekt behöver vi definiera hur de kan jämföras. Det kan vi göra med hjälp av de inbyggda funktionerna __lt__, __gt__ och __eq__.
def __lt__ (self, other):
if self.namn < other.namn:
return True
else:
return False
När kan det vara bra att kunna jämföra objekt?
Jämföra objekt
30
Vi definierar att bilarna ska jämföras baserat på snabbhet
Det är viktigt att definiera __lt__ eller __gt__ för att kunna använda sort() om objekten ligger i en lsita.
Olika typer av attribut och variabler
31
Publika och privata attribut
Vi väljer om vi vill definiera attribut som publika eller privata.
32
class Kontakt:
"""En klass som representerar en kontakt i en telefonbok"""
def __init__(self, namn, tel, pnr):
self.namn = namn
self.telefonnummer = tel
self.__personnummer = pnr
�gurra = Kontakt('Katten Gustaf', '098 889 98', '1976-01-08')
print(gurra.namn)
print(gurra._personnummer)
Komma åt privata attribut – poängen är att vi inte ska komma åt dem!
För att komma åt privata attribut utanför klassen behöver vi definiera åtkomstmetoder (get) och ändringsmetoder (set).
33
class Kontakt:
"""En klass som representerar en kontakt i en telefonbok"""
def __init__(self, namn, tel, pnr):
self.namn = namn
self.telefonnummer = tel
self.__personnummer = pnr
�def uppdatera_personnummer(self, ny_pnr):
self.__personnummer = ny_pnr
def visa_personnummer(self):
return self.__personnummer
uppdatera_personnummer är en set-metod
visa_personnummer är en get-metod
När är det bra att ha privata attribut?
Ibland vill vi inte kunna ändra vissa attribut i andra klasser, det kan förstöra logiken i vårt program.
Privata attribut är ett sätt att kapsla in koden.
34
Detta kommer bli användbart i vår!
Klassatribut
35
class Långord:
�VOKALER = "aeiouyåäö" # VOKALER är ett klassattribut
�def __init__(self, ordet):
self.ordet = ordet
�def antal_vokaler(self):
antal = 0
for bokstav in self.ordet:
if bokstav in Långord.VOKALER:
antal += 1
return antal
�def main():
ordobjekt = Långord("boendeparkeringstillstånd")
print(ordobjekt.antal_vokaler())
�main()
Obs.
klassattribut kan vara både KONSTANTER och variabler!
Klassatribut
36
Du kan exempelvis komma åt VOKALER (från exemplet innan) genom att skriva Långord.VOKALER utanför klassen.
Större program
37
Större program
I större program behöver vi ofta fler klasser för att enklare strukturera upp koden.
Om vi skriver ett program för en hotellkedja behöver vi kanske en klass som representerar ett hotell och en annan klass som representerar ett rum…
38
Hotell-klassen
Attribut
Rum-klassen
Attribut
Det är vanligt att lägga alla klasser i en separat fil
Lägga klasserna i en separat fil…
Det är vanligt att göra en egen modul av klasserna (lägga dem i en separat fil) som du kan importera in i ditt huvudprogram.
39
if __name__ == "__main__":
main()
Om du vill testa klassen så är det väldigt bra att använda if __name__ == ”__main__” syntaxen!
Komihåg: koden som ligger under if __name__ = ”__main__” kommer bara att köras om du kör programfilen som kodraden ligger i, inte om du importerar filen!
Återanvänd din kod!
40
Arv
41
Kapitel 10 i Möbius
Arv
Subklassen ärver alla attribut och metoder från superklassen!
42
class Subklass(Superklass):
class Katt(Djur):
Exempel
Superklassen object
Alla klasser som vi skriver ärver från superklassen object!
43
class Minklass:
class Minklass(object):
Om vi definierar en klass som heter Minklass
Så kommer Python översätta det till Minklass(object)
Rum-klassen
Lyxsvit
44
Attribut
Metoder
Det är vanligt med metoder som frågar om ett attribut ”är”. Dessa returnerar True eller False.
Attribut
Metoder
Observera: lyxsviten har ytterligare några attribut
Överlagring
När vi låter en klass ärva så ärvs alla attribut och metoder vilket är bra. Men i vissa fall vill vi kanske att attributen och/eller metoderna ska bete sig lite annorlunda, då behöver vi överlagra dem.
Det alltid den metoden närmast som är den som gäller.
45
def __str__(self):
sträng = 'Rum ' + self.rumsnummer + ' är '
if self.uthyrt:
sträng += 'uthyrt.'
else:
sträng += 'ledigt.'
def __str__(self):
sträng = 'Svit ' + self.rumsnummer + ' är '
if self.uthyrt:
sträng += 'uthyrt.'
else:
sträng += 'ledigt.'
Rum
Lyxsvit
Varför är dessa olika?
Överlagring
Det alltid den metoden närmast som är den som gäller.
46
Object – klassen som omsluter
alla andra klasser
Rum
Lyxsvit
Om vi har ett lyxsvitobjekt så kommer lyxsvit-str-metoden köras om vi skriver ut objektet
Komma åt superattributen i subklassen
För att slippa initialisera superklassens attribut igen kan vi komma åt dem genom att använda funktionen super().
47
def __init__(self, nr, storlek=2, sängar=1, uthyrt=False):
self.rumsnummer = nr
self.storlek = storlek
self.antal_sängar = sängar
self.uthyrt = uthyrt
def __init__(self, nr, storlek, sängar, uthyrt, havsutsikt, fler_rum):
super().__init__(nr, storlek, sängar, uthyrt)
self.havsutsikt = havsutsikt
self.fler_rum = fler_rum
Rum
Lyxsvit
Arv med geometriska former
48
a
a
a
b
a
b
h
Parallellogram:
Area = h*b
Omkrets = 2*(a+b)
Rektangel:
Area = a*b
Omkrets = 2*(a+b)
Kvadrat:
Area = a*a
Omkrets = 4*a
Vad ärver från vad???
Arv med geometriska former
49
Parallellogram
50
class Parallellogram(object):
def __init__(self, a, b, h):
self.kant1 = a
self.basKant = b
self.hojd = h
def area(self):
return self.hojd*self.basKant
�def omkrets(self):
return 2*(self.kant1+self.basKant)
a
b
h
Rektangel
51
from parallellogram import Parallellogram
�class Rektangel(Parallellogram):
�def __init__(self, a, b):
self.kant1 = a
self.basKant = b
�def area(self):
return self.kant1*self.basKant
a
b
Varför räcker det med att definiera area (förut hade vi med omkrets)?
Kvadrat
52
from rektangel import Rektangel
�class Kvadrat(Rektangel):
�def __init__(self,a):
self.kant1 = a
self.basKant = a
a
a
Varför behöver vi inte definiera area och omkrets?
Arv från en inbyggd klass - Exception
53
class Sjutton(Exception):
pass
�def skriv_tal(antal):
for i in range(antal):
if i == 17:
Raise Sjutton
else:
print(i)
�def main():
try:
skriv_tal(25)
except Sjutton:
print("Det var som sjutton!")
�main()
Vad händer här?
Vi ärver alla egenskaper från klassen Exception, men kallar vårt exception (hemmasnickrade undantag för Errors) Sjutton
Arv från en inbyggd klass - list
54
class Inköpslista(list):
def __str__(self):
sträng = ""
for vara in self:
sträng += " * " + str(vara) + "\n"
return sträng
Vad händer om vi skapar en lista med Ägg, Smör och Mjölk?
Labb 2
Lösenordskollaren
55
Deadline: Fredag 24/11