Python
O razredih
Najprej nekaj o terminologiji. Programi tradicionalno vsebujejo
spremenljivke in funkcije in pogosto uporabljamo eno skupino funkcij za
delo z eno skupino podatkov, drugo skupino funkcij pa za obravnavo
druge skupine podatkov.
Objektno usmerjeno programiranje uvaja novo obliko podatkov, ki mu
pravimo objekt. Ta vsebuje tako spremenljivke, kot tudi
funkcije, ki
delujejo na te spremenljivke. Poznamo še nekaj - razred (class), ki je
abstraktna definicija objekta, v resnici pa ne "obstaja". Če hočemo
uporabiti razred, moramo napraviti (vsaj en) njegov primerek (izvod
oziroma instanco). Vsakemu takemu primerku (instanci) pravimo
objekt.
Razmišljaj tako: Koncept števil je razred. 1, 2
in 3 so vsi
primerki (instance) števil in so torej posamezni objekti, ki
spadajo v
razred števil. Še bolj jasno bo, če gremo h
primerom:
Če hočemo uporabiti objekte, moramo najprej definirati "razred (
"class") objektov, iz katerih izhaja. V Pythonu naredimo to s stavkom
class , izgleda pa to tako:
class Oseba: ime = "Janez Novak" naslov = "Slovenska 120" mesto = "Ljubljana" drzava = "SLO" zip = "1000" email = "janez.novak@arnes.si"
|
Ta razred vsebuje le podatke, vendar to ni narobe. Tak razred lahko
uporabimo (je neka vrsta slovarja...):
hisnik = Oseba() # tvorimo primerek osebe in # novi objekt shranimo pod spremenljivko hisnik print hisnik.ime # izpise "Janez Novak" print hisnik.email # izpise vrednost atributa "email" od hisnika hisnik.email = "hisnik@mss.si" # atributu dodelimo novo vrednost hisnik.drzava = "SI" # za hisnika spremenimo atribut drzava # tako se spremeni le atribut objekta hisnik # Ostali primerki oseb imajo nespremenjen atribut
|
Doslej
smo razred uporabljali kot slovar, vendar z drugo sintakso. Bolj
zanimivo je, če dodamo "metode",
ki so funkcije, specifične za objekt.
class Vozilo: polozaj = 0 hitrost = 0 def premakni(self): self.polozaj = self.polozaj + self.hitrost def pospesuj(self, prirastek): self.hitrost = self.hitrost + prirastek
|
Sedaj
imamo v razredu metode. Te metode so kakor kakšne druge
funkcije, le da
jim je dodan poseben argument - "self". Kako to deluje:
golf = Vozilo() Vozilo.pospesuj(golf,10) while golf.polozaj < 1000: # premikaj vozilo, dokler ne pride tja... Vozilo.premakni(golf)
|
Metodo razreda pokličemo z obliko "razred.metoda
( )". V našem
primeru uporabljamo Vozilo.pospesuj() in Vozilo.premakni(). Prvi
argument, ki ga podamo, je instanca (primerek) vozila, ki ga
uporabljamo, v nasem primeru je to golf. Druge argumente uporabljamo v
skladu z dedfinicijo posamezne funkcije.
Obstaja krajši (in bolj pogost) način klicanja metod, ki sodijo
k
nekemu objektu. Kličemo torej metodo objekta in ne metodo razreda..
golf = Vozilo() golf.pospesuj(10) # klice Vozilo.pospesuj(golf,10) while golf.polozaj < 1000: # premikaj vozilo, dokler ne pride tja... golf.premakni() # klice Vozilo.premakni(golf)
|
Uporabimo torej obliko "objekt.metoda(
)". Python sam interno preslika to kratko obliko klicanja
metod v prej omenjeno daljšo obliko.
Kako nam vse to lahko koristi? Oglejmo si primer. Recimo, da
pišemo
igrico za dva igralca. Pomniti moramo imeni igralcev, njune točke itd.
Namesto, da bi imeli dve imeni igralcev, dve imeni točk itd, uvedemo en
razred in tvorimo z njim dva primerka (dve instanci):
class Igralec: ime = "" tocke = 0 bonus = 0 def racunajTocke(oseba): return oseba.tocke * oseba.bonus #----------------------------------------------------------------- igralec1 = Igralec() igralec1.ime = "Janko" igralec2 = Igralec() igralec2.ime = "Metka" konecIgre = 0 while not konecIgre: print igralec1.ime, "ima stevilo tock", igralec1.racunajTocke() print igralec2.ime, "ima stevilo tock", igralec2.racunajTocke()
|
Tako bi igro zlahka razširili na 3, 4 ali še več igralcev.
Morda se vprašaš. zakaj smo za imenom razreda, ko
smo tvorili nov
primerek razreda, napisali oklepaje. Zato, ker imajo vsi razredi
posebno metodo z imenom "__init__" , ki jo Python pokliče, ko
tvorimo nov primerek
razreda (v računalniškem izrazoslovju pravimo taki metodi
"konstruktor"). Če te metode ne definiramo, jo Python sam definira kot
funkcijo, ki ne naredi nič.
Lahko pa jo sami eksplicitno
definiramo in
v njej zapišemo kakšno kodo, na primer:
class Igralec: def __init__(noviIgralec,novoIme): noviIgralecr.ime=novoIme noviIgralec.tocke=0 noviIgralec.bonus=0 def izracunajTocke(oseba): return oseba.score * oseba.bonus #--------------------------------------------------- igralec1 = Igralec("Janko") igralec2 = Igralec("Metka") konecIgre = 0 while not konecIgre: print igralec1.ime, "ima stevilo tock", igralec1.racunajTocke() print igralec2.ime, "ima stevilo tock", igralec2.racunajTocke()
|
Še vedno nisi prepričan o uporabnosti razredov in
objektov?
Spomni se nerodne uporabe metod "readlines" in"writelines"
in skrbi za znake "end of line". Ali ne bi bilo lepo,
če bi za take podrobnosti skrbel nekdo drug? Tu je
rešitev:
sestavimo
svoj razred "File":
class File: def __init__(self, filename, mode): self.filename = filename self.mode = mode self.file = open(filename, mode) def readlines(self): theLines = self.readlines() for lineNumber in range(len(theLines)): theLines[lineNumber] = theLines[lineNumber][0:-1] return theLines def writelines(self,newLines): for line in newLines: self.write(line) self.write('\n')
|
Razred shranimo v datoteko, na primer z imenom "BetterFile.py".
Sedaj lahko program, ki dela s seznamom imen,
preoblikujemo:
from BetterFile import File def izpisImen( seznam ): for ime in seznam: print ime
def dodajIme( seznam ): print "Vnesi novo ime" novoIme = raw_input() seznam.append(novoIme)
seznamOseb = [] izbira = 0 while izbira != 5: print "1) Beri datoteko" print "2) Izpis imen" print "3) Dodaj ime" print "4) Shrani seznam" print "5) Konec" choice = input() if izbira == 1: seznamOseb = File("osebe", "r").readlines() elif izbira == 2: izpisImen( seznamOseb ) elif izbira == 3: dodajIme( seznamOseb ) elif izbira == 4: File("osebe", "w").writelines( seznamOseb )
|
Vrstico
File("osebe", "w").writelines(seznamOseb)
|
Bi lahko zamenjali z
izhodnaDatoteka = File("osebe", "w") izhodnadatoteka.writelines(seznamOseb)
|
Obe kodi sta ekvivalentni. Klic "File()" vrne objekt. S tem objektom
lahko delamo enako, kot z objekti, shranjenimi v spremenljivkah.
Objekte torej lahko eksplicitno poimenujemo.
Posvetimo se objektno usmerjenemu programiranju (OOP). Kot primer
vzemimo program, ki potrebuje predstavitev kompleksnih števil.
Kompleksna števila imajo dva dela: realni in imaginarni. Oba
dela sta
navadni števili. Radi bi imeli mopžnost tvorbe kompleksnih
števil, na
primer na naslednji način:
Pri tem je 4.5 realni del in 1.9 je imaginarni delt. Radi bi tudi
dobili absolutno vrednost kompleksnega števila
Pri tem je absolutna vrednost definirana kot kvadratni koren vsote
kvadratov realne in imaginarne komponente (podobno, kot velja c2
= a2
+ b2). Da bi to lahko storili, moramo verjetno uvoziti
(import) modul "math", kar bi omogočilo dostop do funkcije "sqrt".
(Oglej siLibrary
Reference). Poleg tega želimo seštevati in odštevati
kompleksna
števila. tako kot kaže spodnji primer:
y = Complex(-8.0, 2.3) z = x.minus(y) w = y.plus(z) if x.abs() != abs(): print "napaka!"
|
"plus" jemlje drugo kompleksno število kot argument in
vrača
tretje komleksno število. Realni del tretjega števila je
enak vsoti
realnih delov prvotnih dveh števil. Imaginarni del tretjega
števila je
enak vsoti imaginarnih delov prvotnih števil. Modobno deluje
"minus".
Napiši torej modul, morda z imenom "Complex.py" in ga
uporabi
oziroma preveri v drugem programu (morda z imenom
"testComplex.py").