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:

    x = Complex(4.5, 1.9)

Pri tem je 4.5 realni del in 1.9 je imaginarni delt. Radi bi tudi dobili absolutno vrednost kompleksnega števila

    abs_x = x.abs()

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").