Klasser

Klasser i Python

Klasser kan ses som skitser over objekter.

Objekt Orienteret Programmering

Klasser og objekter kalder man også for objekt orienteret programmering eller bare OOP. Konceptet kan, forsimplet, først og fremmest ses i måden, som klasserne kan genbruges i programmer, om end OOP kan benyttes til en række andre formål end genbrug.

Klasser – når vi skitserer
Først og fremmest skriver man klasser og definerer hvilke indhold, de skal have. Man kan sammenligne en klasse med en skitse eller en model over noget. Det kan fx være en klasse, som vi kalder Hund og efterfølgende skitserer vi hundens adfærd. Den skal sandsynligvis have et navn, en alder og måske har vi også behov for at beskrive hvilken race den er. Derudover skal vores hund måske også kunne gøre noget. Den giver fx pote og lystrer til “sit”.

Ovenstående klasse skal derfor forstås som model/skitse over en hund.

Klassens data passeres videre til objektet.

Objekter
Når vi har skrevet vores klasse og modelleret hvad den skal indeholde, passerer vi klassens data videre.
Når vi giver en klasses data videre kalder vi modtager af data for object instances eller bare et object.
Vi kan fx kalde klassens, Hund, for objektet min_hund. Vi tildeler hermed objektet, min_hund, data fra klassen, Hund, og definerer følgende parametre: navn, alder og race.

Derudover kan vores objekt give pote og lystre til “sit”. Alt dette gennemgår vi i nærværende kapitel.

Bemærk, at klasser er et komplekst område, at bevæge sig ind på. Man skal have kendskab til funktioner inden klasser overhovedet giver mening. Og da objekt orienteret programmering må forventes at stille mange spørgsmål, anbefaler jeg at eleverne har adgang til en underviser i starten.

Fundamentet skal være på plads før vi bygger opad.

Ligesom de gamle ægyptere byggede pyramiderne fra grundigt fra grunden og opefter, er jeg tilhænger af samme princip. Det grundlæggende skaber fundamentet. Og når fundamentet er på plads, kan man altid bygge opad.

Vores første klasse

Allerførst vil vi skrive en klasse, som giver os værdien 10.

class Number10(object):
    nummer = 10

Klasser skrives konventionelt set altid med stort bogstav i begyndelsen. (Modsat funktioner som konventionelt skrives med lille bogstav).

I eksemplet skriver vi variablen nummer som vi tildeler værdien 10. Vi har nu skrevet vores første klasse. Gem din fil som en python-fil. Fx eksempel1.py.

Inde i vores konsol importerer vi nu vores klasse og tildeler objektet num klassens data. Skriv flg.:

num = Number10()


Herefter ønsker vi at printe nums data ud:

 print(num.nummer)

num som nu er et objekt(object instance) og har fået tildelt data fra klassen Number10, printer værdien 10 ud til os.


Du kan også bare skrive: num.nummer – dette giver også 10. Opsummeret hedder vores klasse Number10 og vores objekt num.

Dot notation
Et objekt i Python indhenter data fra den tildelte klasse( i dette eksempel Number10). Herefter kan man ved hjælp af punktum indhente de data, som objektet skal bruge. Det er altså det smarte ved klasser, at i modsætning til funktioner, så er en klasse en model af noget, hvor du kan vælge og fravælge funktioner, variabler, klasser etc.

Test dig selv:

  • Tildel et nyt objekt, som du kalder noget andet en num, klassen Number10.
  • Prøv at addere dit nye objekt med 10
  • Prøv at skrive en ny klasse som indeholder et andet tal. Tildel dets data til et nyt objekt.
  • Prøv at skrive en string i stedet for et tal.

Koderne på github: https://github.com/atlemgw/klasser/blob/master/1_eksempel

Eksempel 2

I dette eksempel går vi nu lidt dybere ind ned i klasserne.

class Docstring(object):
    """Denne klasse indeholder en docstring og ikke andet"""
    pass

Lidt om koderne

Ovenstående program er en klasse, som deffineres således: class Docstring(object):
Efterfølgende skriver vi en docstring, der beskriver klasses indhold. Mere herom længere nede i afsnittet.
Til sidst benytter vi pass, som benyttes, når man ikke vil eksekvere en kommando.
Kort og godt er det eneste vores program indeholder en docstring.

Gem herefter klassen som docstring.py og kør den i din konsol. Der er umiddelbart ikke noget at se. Nu skriver du følgende:

doc = Docstring()

Her fungerer doc nu som et objekt for klassen Docstring, og doc indeholder nu klassen Docstrings data.
Den eneste data som Docstring imidlertid indeholder er en docstring, som er super vigtige at bruge, når vi skriver klasser, idet det kan være til stor gavn for andre, som skal benytte vores klasser og for den sags skyld os selv, hvis vi skulle glemme hvad en klasse indeholder.
Man kan med lidt god vilje sammenligne en docstring med en kommentar.

I din konsol skriver du nu:

doc

Den eneste data vi modtager i konsolen er <main.Docstring at 0x7f97b8f67f60>. Det fortæller os grove træk, at vores objekt(main = doc), indeholder Docstrings data, samt hvilken adresse den ligger på i RAM’en.
Vi søger derfor hjælp om klassen og skriver følgende:

help(doc)

Vi modtager følgende output:

Help on Docstring in module __main__ object:

class Docstring(builtins.object)
| Denne klasse indeholder en docstring og ikke andet
|
| Data descriptors defined here:
|
| __dict__
|    dictionary for instance variables (if defined)
|
| __weakref__
|    list of weak references to the object (if defined)

Vores docstring fremgår nu tydligt. Og den fortæller os at denne klasse ikke indeholder andet end vores docstring.

Test dig selv:

  • Skriv en kommentar ovenover din docstring, hvor du skriver, præcis det samme som din docstring.
    Printer Python din kommentar ud i konsollen eller printer Python en docstring ud. Hvem kan læse din
    kommentar og hvem kan læse din docstring?
  • Når du tester dem, så kig grundigt på print(doc) og print(doc2). Ligger de på samme
    adresse i computerens RAM?
  • Tag udgangspunkt i vores 1. eksempel og skriv en docstring til den og beskriv hvad den gør:
    class Number10(object):
    nummer = 10

Koden på Github: https://github.com/atlemgw/klasser/blob/master/2_eksempel

Eksempel 3

class Info(object):
    """Denne klasse indeholder en model over indhentning af en
       persons navn, adresse og telefonnummer"""
    def __init__(self, navn, adresse, telefon):
        self.navn = navn
        self.adresse = adresse
        self.telefon = telefon

Lad os se på, hvad denne klasse skitserer. class Info(object): definerer navnet på vores klasse, som vi kalder Info.
Efterfølgende skriver vi en docstring, der fortæller, hvad klassen modellere.
Herefter skriver vi en method, som minder meget om en funktion. Det er da heller ikke unormalt at høre, at nogle kalde dem funktioner mens andre siger methods. Det er ligemeget så længe vi ved, hvad vi laver!

Contructor : __init__
__init__ er ny for os. __init__ er også hvad vi kalder en objekt orienteret constructor. Den igangsætter vores method, og tildeler objektet de tre variabler, navn, telefon og adresse.

self
Vi benytter også parameteret self, som er vores refference til klassen(i dette eksempel Info), og som benyttes til at give variabler adgang til klassen. I vores eksempel er der således tale om variablerne navn, adresse og telefon. Det betyder kort og godt, at vi kan kalde på navn, adresse og telefonnummer ved at give vores objekt klassen info, som indeholder de respektive variabler.

Lad os se på følgende eksempler:

min_info = Info("Atle", "Andeby", 12345678)
print(min_info.navn)
print(min_info.adresse)
print(min_info.telefon)

Vi tildeler først objektet min_info klassen Info og de dertilhørende data.
Herefter printer vi navn, adresse og telefon ud ved at skrive print(min_info.navn) osv.

Test dig selv:

  • Lav en ny klasse, som indeholder superligaens top-3.
  • Du har nu lavet en fodbold-klasse. Gå ind på http://www.superliga.dk/ og giv din klasse data fra
    superligaens top-3. I skrivende stund er der tale om FCM, FCK og RFC. Netop dette eksempel viser, hvorfor klasser er praktiske og skal ses som skitser, der hele tiden kan ændres. Prøv eksempelvis at vende tilbage til superliga.dk og se om holdene har ændret placering.
  • Skriv nu et print, som skal udføre følgende sætning med variablerne fra din fodbold-klasse: ”FCM
    ligger nummer 1, FCK nummer 2 og RFC ligger nummer 3 i superligaen”- Lav en klasse, hvor du skriver den kommende 5-døgnsprognose fra DMI:
    https://www.dmi.dk/lokation/show/DK/2618425/K%C3%B8benhavn/

Koden på Github: https://github.com/atlemgw/klasser/blob/master/3_eksempel

Eksempel 4

Vi har nu set på methods og kender principperne bag. En klasse kan indeholde mange methods. Vi vil i dette eksempel skrive vores main(init)-method samt en ekstra method.

class Typer(object):
    """Klasse der er modelleret til, at opnå forståelse for
       variabler af forskellig art i en klasse"""
    
    def __init__(self, integer=10, string="min string", floating=254.2846, boolean=True):
        """Method som indeholder default værdier i integers, strings,
        
         floats og booleans"""
         self.integer = integer
         self.string = string
         self.floating = floating
         self.boolean = boolean
    
    def data(self):
        """Dataset om int, str, float og bool"""
        print()
        print("Integer:", str(self.integer))
        print("String:", self.string)
        print("Float:", str(self.floating))
        print("Boolean:", str(self.boolean))
        print()
        print("Integer:", str(self.integer), "\nString", self.string, "\nFloat", str(self.floating), "\nBoolean:", str(self.boolean))

Vi deffinerer klassen som Typer, da klassen kommer til at indeholde data med strenge, floats, integers og booleans.
Vi skriver en docstring under klassen og de to methods.


Af praktiske grunde har jeg valgt at lave default værdier vha assignment(=), for at eksemplificere hvordan dette kan bruges til at spare tid, så man er fri for at skrive nye værdier ind i de respektive parametrer om igen. Default-værdierne kan altid ændres som du har lyst til.
Den første method tildeler klassen de 4 variabler integer, float, string og boolean.
Vores 2. method def data(self): overfører variablerne og lader os printe dem. Her er det relevant at iagttage, at vi har tildelt variablerne værdier af tre andre typer end string, nemlig integer, float og boolean. Derfor konverteres disse vha. str()-funktionen.

Test dig selv:

  • Lav en tilsvarende klasse som eksemplet, men som du adderer, subtrahere, dividere og multiplicere i
    din print-funktion.
  • Lav en helt ny klasse, som indeholder 3 methods.

Koden på Github: https://github.com/atlemgw/klasser/blob/master/4_eksempel

Note: Hvis noget herinde virker velkendt, skyldes det at eksemplerne er sakset fra mit hæfte om klasser og objekter til gymnasiebrug.