Start | klasser
 

Klasser



Tänka i objekt

Säg att du ska tillverka ett dator-program som håller koll på eleverna i en skola. Dvs, uppgifter om eleverna alltså.

En elev har bl.a. ett namn, både förnamn och efternamn (attribut). Vi måste veta ett telefonnummer också, så vi kan ringa eleven. Vi måste också veta vilken klass eleven går i. Vi måste veta mycket mer såklart, man vi begränsar oss lite i detta exempel. Vi vill kunna skriva ut elevens uppgifter (metod).


Skapa en klass

Vi tänker oss en mall (prototyp, skiss, ritning) med den data som vi vill lagra. Vi kallar denna mall för klass. I denna klass listar vi alla de attribut (variabler) vi behöver, eventuellt med standardvärden (default-värden).

Observera att denna klass bara är en mall. Vi kan inte lagra något där. För att kunna använda vår klass ("mall") så skapar vi ett eller flera eller tusentals instanser, alltså objekt med korrekt info/data, utifrån den.

Skapa ett objekt av vår klass gör vi med nyelev = Elev() här nedan.
class Elev: fornamn = "Ej imtatat" efternamn = "Ej imtatat" tele = "okänt" klass = "okänt" nyelev = Elev() print(nyelev.fornamn)
Objektet nyelev kan vi nu använda. Objektet huserar nu alla de olika variabler vi angav i vår klass.

I ovan exempel får vi svaret "ej inmatat", när vi kör koden, vilket är rimligt, då vi skapade en elev men angav inget nytt namn på eleven, så det blev default -värdet som låg kvar. Vill vi ange namn får vi göra såhär.
class Elev: fornamn = "Ej imtatat" efternamn = "Ej imtatat" tele = "okänt" klass = "okänt" nyelev = Elev() nyelev.fornamn = "Anders" nyelev.efternamn = "Andersson" nyelev.tele = "0707070707" nyelev.klass = "TKINT" print(nyelev.fornamn)
Det finns ett proffsigare sätt att initiera våra skapade objekt, som också bjuder på en del extrafunktioner kommer det visa sig (längre fram).

__init__ -iera en klass

Konstruktionen är följande, vi lägger till en __init__ i klassen. Det brukar heta konstruktor (tänk konstruktör, skapare). Men det är ju redan tillräckligt tydligt, eller hur? Du ser ju själv att det står __init__. Det är precis vad vi gör!

Genom att lägga till en initiering i klassen, så kan vi parallellt med att vi skapar objektet också tilldela alla dess värden. Vi skapar en instans av klassen, i detta fall Anders Andersson. Han är en instans.
class Elev: def __init__(self, f, e, t, k): self.fornamn = f self.efternamn = e self.tele = t self.klass = k nyelev = Elev("Anders","Andersson","070707","TKINT") print(nyelev.fornamn)
Samtidigt som vi skapar ett nytt Elev -objekt nyelev = Elev( ... ), så kommer alltså våra argument skickas över till __init__ som i sin tur skickar in dem i vår Elev -instans. Så när kodraden är utförd är instansen nyelev full med data.

Self är en referens som refererar till själva klassen. Tänk såhär: I alla funktioner så blir variablerna lokala. Så i vår initieringsfunktion, om vi inte refererade till omgivande klassen, så skulle alla data stanna i och försvinna med initieringsfunktionen.

På detta sätt överförs data till instansens variabler (instansvariabler) och blir kvar där.

Vi kan skapa många instanser av vår klass och då blir det också tydligare vad själv poängen med detta är.
class Elev: def __init__(self, f, e, t, k): self.fornamn = f self.efternamn = e self.tele = t self.klass = k elever = [ Elev("Anders","Andersson","070707","TKINT"), Elev("Per","Persson","060708090","TKINT"), Elev("Nils","Nilsson","706050403","TKINT"), Elev("Ols","Olsson","09090909","TKINT") ] print(elever[0].fornamn) print(elever[1].fornamn) print(elever[2].fornamn) print(elever[3].fornamn)
En klass kan innehålla metoder. Så när en ny instans av klassen skapas, så hänger den här metoden med och kan användas. Vi kan tex skriva en metod skrivut() för att skriva ut all data. Vi lägger alltid till parametern self när vi skriver metoden, därför att den parametern läggs till automatiskt vid anropet. Self behövs så att vi kan referera instansens variabler, så att vi får tag i dem och kan skriva ut dem.
class Elev: def __init__(self, f, e, t, k): self.fornamn = f self.efternamn = e self.tele = t self.klass = k def skrivut(self): print(f"{self.fornamn} {self.efternamn} {self.klass}") elever = [ Elev("Anders","Andersson","070707","TKINT"), Elev("Per","Persson","060708090","TKINT"), Elev("Nils","Nilsson","706050403","TKINT"), Elev("Ols","Olsson","09090909","TKINT") ] for i in range(0,len(elever)): elever[i].skrivut()
Vi kan nu loopa över alla våra instanser och printa ut dess data.

Printbar klass med __str__

Visst hade det varit coolt att kunna skriva ut vår egna klass med den vanliga print -funktionen? Print kan ju skriva allt annat här i världen, så varför inte vår egna klass? Det finns något i python som kallas dundermetoder. Om du bara ska lära dig en enda dundermetod, så är det __str__. Se nedan:
class Elev: def __init__(self, f, e, t, k): self.fornamn = f self.efternamn = e self.tele = t self.klass = k def __str__(self): return(f"{self.fornamn} {self.efternamn}\n{self.klass}") elever = [ Elev("Anders","Andersson","070707","TKINT"), Elev("Per","Persson","060708090","TKINT"), Elev("Nils","Nilsson","706050403","TKINT"), Elev("Ols","Olsson","09090909","TKINT") ] for elev in elever: print(elev)
Om vi definierat en __str__ så kommer den anropas ifall vi använder print. Visst är det coolt!

instans & klassvariabler

Vi hade kunnat tänka oss nedanstående. Eftersom alla elever går på samma skola hade vi kunnat göra såhär. Alla instanser skulle då även ha en variabel som heter skola med värdet CTH. Vi kallar denna variabel för klassvariabel, eftersom den ligger på klass-nivå. Medans fornamn, efternamn, tele och klass ligger på instansnivå, dvs de initieras med egna värden varje gång en ny instans skapas.
class Elev: skola = "CTH" def __init__(self, f, e, t, k): self.fornamn = f self.efternamn = e self.tele = t self.klass = k def __str__(self): return(f"{self.fornamn} {self.klass} {self.skola}") elever = [ Elev("Anders","Andersson","070707","TKINT"), Elev("Per","Persson","060708090","TKINT"), Elev("Nils","Nilsson","706050403","TKINT"), Elev("Ols","Olsson","09090909","TKINT") ] for elev in elever: print(elev)
Men vi kan fortfarande komma åt vår klassvariabel med self vilket du ser i utskriften ovan och vi hade omvänt kunna sätta ett värde på den via init.

Det är mest för att kunna prata om det som man kallar variabler på klass-nivå för klassvariabler (nära ordet class i classen) och variabler på instans-nivå för instansvariabler (ligger i __init__).

Tanken är att klassvariabler används för sådant med mycket default -värden och instansvariabler för det som är unika för varje instans.

Mer exempel.
class Fyrkant: def __init__(self, bredd, hojd): print("En instans av klassen fyrkant skapas.") self.bredd = bredd self.hojd = hojd def get_area(self): return self.bredd * self.hojd def get_omkrets(self): return 2 * self.bredd + 2 * self.hojd min_fyrkant = Fyrkant(10,20) print("area=",min_fyrkant.get_area()) print("omkrets=",min_fyrkant.get_omkrets())

public, protected och private

Om du kommer från något annat programspråk, t.ex. C++ eller C#, Java eller vad som helst egentligen, så finns det oftast keyword i språket för att markera om ett attribut eller metod är private, protected eller public. Det finns inga sådana keyword i python. Alla metoder och attribut är som standard publika och kan kommas åt av klasser som ärver. Men det finns en konvention att; om man vill skydda en metod eller attribut i python så namnger man med ett underscore innan.
class Person: def __init__(self, namn, hemlis): self.namn = namn self._hemlis = hemlis p = Person("Olle", "har en hamster") print(p.namn) print(p._hemlis)
I ovan klass har vi alltså antytt att _hemlis är en protected grej. Men det går fortfarande att snoka! Det här med underscore är bara en indikation att man inte ska röra attributet ifråga - inte något förbud.

Vill du höja nivån av skydd, dvs det som motsvarar private i andra programspråk, så kör du 2 stycken underscore.
class Person: def __init__(self, namn, hemlis, megahemlis): self.namn = namn self._hemlis = hemlis self.__megahemlis = hemlis p = Person("Olle", "har en hamster", "kod till lås: 1234") print(p.namn) print(p._hemlis) print(p.__megahemlis)
Om du kör ovan så får du ett error. Eller rättare sagt, koden skriver ut namn och _hemlis men därefter får du ett AttributeError. Det går alltså inte få reda på megahemlisen. Dvs, såtillvida man inte är en hacker och känner till att python, för att det skall bli privat, tillämpar namemangling (namn-förvrängning). Vi kan alltså få reda på den privata variabeln ändå, se nedan.
class Person: def __init__(self, namn, hemlis, megahemlis): self.namn = namn self._hemlis = hemlis self.__megahemlis = megahemlis p = Person("Olle", "har en hamster", "kod till lås: 1234") print(p.namn) print(p._hemlis) print(p._Person__megahemlis)
Men det är mer privat än tidigare. När allt kommer kring, så det här med public, protected och private handlar om att koden ska bra, det är inte på liv och död.

inbyggda klassfunktioner

Klicka för mer exempel hur dessa inbyggda klassfunktioner fungerar.

setattr(o,a,v)Sätt nytt värde på attr
delattr(o,a)Tag bort attribut
getattr(o,a,d)Hämta värdet på attribut
hasattr(o,a)True om attribut existerar
issubclass(o,sub)True om objekt är subklass
super(o, sub)Referens till superklass

Lite frågor

Vad är self ?
  Self refererar till självet, som psykologen Sigmund Freud beskrev det
  Self är en referens till klassen och vi kommer åt klassens variabler genom self, t.ex. self.name
  Self refererar till programmeraren, det är självet som sitter och kodar datorprogram
Vad menas med attribut?
  Klassens olika variabler
  Hur klassen ser ut
  Utmärkande egenskap
Vad är en metod?
  Ett kökssystem från IKEA
  En funktion i en klass kallas metod
  Det sätt man gör något på
Vad är klassvariabel
  En variabel som skapats i klassen, vanligtvis för default -värden
  En variabel som skapats med self i __init__
  En instans av objektet
Vad är protected?
  Ett attribut som inleder med _ underscore för att markera att den inte skall röras.
  En klass som har ett skydd i form av en underscore.
Vad är en instans?
  Tingsrätten är en första instans
  Ett exemplar av en klass
Vad är en instansvariabel?
  En variabel som skapats i klassen
  En variabel som skapats med self i __init__ när instansen skapades


18.917083740234 ms