Objekt Orienteret Programmering i Python

Page 1

Klasser i Python Hæftet indeholder en kort introduktion til at skrive klasser i Python.

Klasser Man kan sammenligne en klasse med en skitse eller en model over noget – oftest virkelighedsnært. Det kan fx være en klasse, som vi kalder Hund og efterfølgende skitserer vi dens adfærd. Den skal sandsynligvis have et navn, en alder og måske har vi også behov for at beskrive hvilken race hunden er. Derudover skal vores hund måske også kunne gøre noget. Den giver måske pote og lystrer til "sit". Alt dette indeholder klassen data over og skal derfor forstås som vores model/skitse over en hund.

Objekter Når vi har skrevet vores klasse og modelleret hvad den skal indeholde, giver 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 bestemme at kalde klassens objekt for min_hund. Vi giver herefter objektet, min_hund, data fra ovenstående klasse og deffinerer følgende parametrer: navn, alder og race. Derudover kan vores objekt give pote og lystrer til sit. Alt dette - og meget andet - gennemgås I nærværende hæfte.

Noter Hæftet har været brugt til mine egne undervisninger og bør først benyttes, når den studerende er fortrolig med funktioner i Python. Fordi objekt orienteret programmering(OOP) må tolkes som et komplekst felt, anbefaler jeg at de studerende ikke skal nøjes med dette hæfte alene, men omhyggeligt introduceres til stoffet ved at have adgang til en underviser, som kan hjælpe og bistå med at formidle viden.

Kontakt Jeg hedder Atle Winther og kan kontaktes på atlewinther@protonmail.com Koderne findes på https://github.com/atlemgw/klasser


Ligesom de gamle ægyptere byggede pyramiderne grundig opt fra grunden, er jeg tilhænger af samme princip. Det grundlæggende skal først på plads. Og når vi har lagt fundamentet, er der plads til at bygge op.

1. eksempel 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” og tildeler den 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 dens data til num, som nu er klassens object instance. Skriv flg.:

num = Number10()

Herefter ønsker vi at printe nums data ud:

print(num.nummer)

Her printes klassen, Number10, altså ud vha. vores objekt, som printer klassens variabel nummer.

10

Opsummeret hedder vores klasse Number10 og vores objekt num.

Dot-notation Et objekt i Python indhenter data fra den tildelte klasse.. Herefter kan man ved hjælp af punktum(kaldet dot-notation) indhente de data, som objektet skal bruge. Det er altså det smarte ved klasser; 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


2. eksempel I dette eksempel går vi nu lidt dybere ned i klasserne. I din editor skriver du:

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, som beskriver vores 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.

I konsollen 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 dermed klassen Docstrings data. Den eneste data som Docstring imidlertid indeholder er en docstring, som er nyttig, når vi skriver klasser. Det kan nemlig være til stor gavn for andre, som skal benytte vores klasse og for den sags skyld også os selv, hvis vi skulle glemme hvad en klasse indeholdt. 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> som kort og godt fortæller os, i grove træk, at det er vores objekt(__main__ = doc og visa versa), som indeholder Docstrings data, samt hvilken adresse den ligger på i RAM.

Vi bliver derfor nødt til at søge 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 netop 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 på 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

3. eksempel 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): deffinerer navnet på vores klasse, som vi har kaldt Info. Efterfølgende skriver vi en docstring, der fortæller, hvad klassen modellere.


Derefter skriver vi en method, som minder rigtig meget om en funktion. Det er ikke unormalt at høre nogle kalde methods for funktioner funktioner. __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. Vi benytter også parameteret self, som er vores refference til klassen(i dette eksempel Info), og som benyttes til at give adgang til klassen. I vores eksempel er navn, adresse og telefon, de argumenter vores instance variable skal bruge. 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(self.navn, self.adresse og self.telefon). 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 kan vi printe 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


4. eksempel Vi har nu set på methods og kender princippet 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, når man tester klassen. Default-værdierne kan altid ændres og fjerne, helt som du har lyst til.

Den første method tildeler klassen integer, float, string og boolean.

Vores 2. method def data(self): overfører instance variablerne og lader os printe dem til skærmen. Her er det vigtigt 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


Projekt Vi vil nu se på et større projekt, hvor vi dels bruger vores nuværende viden samt bygger på. Projektet handler i sin enkelthed om, hvordan vi kan beskrive et flys funktioner og bygge på med nye methods. Vi kender i forvejen projektets første eksempel fra tidligere eksempler, men efterhånden kommer vi til at bygge mere på vores kundskaber, så vi til sidst har skabt et grundlæggende fundament for klasser og objekter i Python.

Fly version 1: class Fly(object): """Beskrivelse af et fly"""

def __init__(self, model, produceret, bruger): """Initiliserer flymodel, produceret(år og producent) samt brugeren af flyet(flyselskab)""" self.model = model self.produceret = produceret self.bruger = bruger

def info(self): # kort information af fly """Kort info af flyet med print af model, årgang og producent og brugeren""" print_info = "{2} {1} {0}".format(self.model, self.produceret, self.bruger) #Format benyttes til at vende om på rækkefølgen af de tre argumenter return print_info.upper() #upper tildeler store bogstaver

Herefter tester du din klasse:

mit_fly = Fly("Airbus a380", 2010, "Lufthansa") print(mit_fly.info()) Der skulle nu meget gerne stå: LUFTHANSA 2010 AIRBUS A380 Github: https://github.com/atlemgw/klasser/blob/master/projekt/fly_v1


Fly-version 2: Vi kunne i øvrigt også godt tænke os at se på, hvor langt vores airbus kan flyve på en fuld tank. Vi går derfor ind på AirBus hjemmeside og finder vores AirBus: https://www.airbus.com/aircraft/passengeraircraft/a380/innovation.html#details Vi ser, under perfomance, at den kan flyve 14.800 km på en fuld tank. Lad os omskrive klassen en smule: class Fly(object): """Beskrivelse af et fly"""

def __init__(self, model, produceret, bruger): """Initiliserer flymodel, produceret(år og producent) samt brugeren af flyet(flyselskab)""" self.model = model self.produceret = produceret self.bruger = bruger self.afstand = 0 #Antal kilometer flyet flyver på en tur

def info(self): # kort information af fly """Kort info af flyet med print af model, årgang og producent og brugeren""" print_info = "{2} {1} {0}".format(self.model, self.produceret, self.bruger) # #Format benyttes til at vende om på rækkefølgen af de tre argumenter return print_info.upper() #upper tildeler bogstaverne versaler

def tank(self): """Printer hvor langt flyet har fløjet""" print("{} har fløjet {} kilometer på turen".format(self.model, self.afstand))


I vores editor skriver vi nu:

mit_fly = Fly("Airbus a380", 2010, "Lufthansa") print(mit_fly.info()) mit_fly.tank() Vi ser nu den tidligere info fra fly-version 1, og vi ser også hvor langt flyet har fløjet. Men lad os prøve at ændre på kilometerne som flyet har fløjet. Her kan vi manuelt gå ind og omskrive dem, hvilket jo præcist er det vi har behov for. Vores objekt har allerede fået tildelt sin model, producent/årgang samt brugeren(flyselskabet). I stedet for at ændre instance variablen self.afstand i klassen, skriver vi i stedet: mit_fly.afstand = 1200 mit_fly.tank() Lad os i næste eksempel omskrive klassen, så vi i stedet for at skrive til en attribute, i stedet skriver til en funktion. Vi skal derfor lave en ny method til klassen.

Fly-version 3 #FLY_V3.PY class Fly(object): """Beskrivelse af et fly""" def __init__(self, model, produceret, bruger): """Initiliserer flymodel, produceret(år og producent) samt brugeren af flyet(flyselskab)""" self.model = model self.produceret = produceret self.bruger = bruger self.afstand = 0 #Antal kilometer flyet flyver på en tur def info(self): # kort information af fly """Kort info af flyet med print af model, årgang og producent og brugeren""" print_info = "{2} {1} {0}".format(self.model, self.produceret, self.bruger) # #Format benyttes til at vende om på rækkefølgen af de tre argumenter


return print_info.upper() #upper tildeler bogstaverne versaler def tank(self): """Printer hvor langt flyet har fløjet""" print("{} har fløjet {} kilometer på turen".format(self.model, self.afstand)) def opdater_km(self, kilometer): """Opdaterer antallet af kilometer""" self.afstand = kilometer Vi har nu lavet en ny method: opdater_km. Her indtaster vi antallet af kilometer. Vores allerede eksisterende self.afstand assignes til kilometer. Vores method tank får tildelt samme værdi som vi tildeler kilometer, idet kilometer nu er tildelt samme variabel som self.afstand.

mit_fly = Fly("Airbus a380", 2010, "Lufthansa") print(mit_fly.info()) mit_fly.tank() print() mit_fly.afstand = 1200 mit_fly.tank() print() mit_fly.opdater_km(800) mit_fly.tank() Test dig selv: Prøv at udvikle en kode, som gør det umuligt at ændre antallet af kilometer i negativ retning. Man kan jo ikke opdatere en flyvetur fra at helikopteren har fløjet 1200km til 800km. Den kan altså kun opdateres positivt til en talværdi på over 1200 km. I klassen Fly kunne det overvejes om der er behov for en method, der kan tilpasses passagerer og deres behov for at kunne følge med i flyvningen. Fx har mange fly en skærm, der viser altitude, heading, kompas, temperatur og hastighed. Kan man eventuelt lave den som en selvstændig klasse(man kan måske bruge super-funktionen)?


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.