Objektno programiranje je resna tema, ki je ni mogoče kar tako odpredavati v dveh tednih in se ga ni mogoče kar tako naučiti v dveh mesecih. Ker je vaš študij kratek in boste te reči kmalu potrebovali pri drugih predmetih, vam moramo tule predstaviti osnove. Ker vam mora priti stvar v kri, pa ne pričakujmo čudežev. Pri tem predmetu se boste naučili zgolj nekaterih tehnikalij, ne pa vsega, kar v zvezi s tem ponuja Python, vsega, kar ponujajo drugi jeziki in, predvsem, filozofije, ki je za tem.
Definirajmo razred Oseba. Vsaka oseba ima
ime in spol (Z ali
M); oba podatka podamo kot argumenta konstruktorju. Oseba
zna pozdravljati: metoda pozdravi izpiše
Pozdravljeni, jaz sem {}, kjer namesto {}
vstavi svoje ime.
class Oseba:
def __init__(self, ime, spol):
self.ime = ime
self.spol = spol
def pozdravi(self):
print("Pozdravljeni, jaz sem {}".format(self.ime))Tu smo mimogrede prvič srečali konstruktor (metodo
__init__) z argumenti. Želva je imela konstruktor brez
argumentov; novo želvo smo naredili s Turtle() in vedno je
stala na sredini, obrnjena navzgor, vidna in s spuščenim peresom. Pri
osebah ne bomo imeli "začetnega imena" (Janez Novak?!), temveč bomo
morali ob konstrukciji objekta Oseba vedno povedati tudi
njeno ime in spol.
benjamin = Oseba("Benjamin", "M")Vse, kar naredi konstruktor, je, da prepiše vrednost argumenta v (istoimenski) atribut objekta. Se pravi, shrani argument v objekt.
Osebe znajo tudi vljudno pozdravljati:
benjamin.pozdravi()Pozdravljeni, jaz sem Benjamin
Na fakulteti sta, poenostavljeno povedano, dve vrsti oseb, študenti
in učitelji. Za študente beležimo njihove ocene pri posameznih
predmetih. Ocene bomo pisali v slovar, katerega ključi bodo imena
predmetov, vrednosti pa ocene pri teh predmetih. Študenta je mogoče
oceniti, zato bo imel metodo oceni, ki ji bomo kot argument
podali ime predmeta in oceno, pa bo zapisala to v slovar. Poleg tega bo
imel funkcijo poprecje, ki bo izračunala njegovo poprečno
oceno.
Poleg tega pa imajo tudi študenti ime in spol, in tudi pozdravljati
znajo. Z drugimi besedami: študent je vrsta osebe. Študent ima
vse lastnosti osebe in zna vse, kar znajo osebe, poleg tega pa še nekaj
več (beležiti ocene in računati poprečja). Zato bomo razred
Student izpeljali iz razreda Oseba
(Angleži bi rekli Student is derived from
Oseba). Student bo podedoval vse, kar
ima Oseba (Student inherits attributes and
methods from Oseba). Razred Oseba bo
prednik (parent) razreda Student.
Kako to storimo? Zelo preprosto: ko definiramo razred
Student, v oklepaju dodamo še razred, iz katerega naj bo ta
izpeljan.
class Student(Oseba):
def __init__(self, ime, spol):
super().__init__(ime, spol)
self.ocene = {}
def oceni(self, predmet, ocena):
self.ocene[predmet] = ocena
def poprecje(self):
if not self.ocene:
return 0
return sum(self.ocene.values()) / len(self.ocene)Najprej preverimo, ali reč deluje.
rebeka = Student("Rebeka", "Z")
rebeka.oceni("Programiranje 1", 10)
rebeka.oceni("Uvod v racunalnistvo", 9)
rebeka.poprecje()9.5
Sestavimo tri študente, zložimo jih v seznam in vsakemu študentu dodelimo deset naključnih ocen pri naključno izbranih predmetih.
from random import choice, randint
s1 = Student("Ana Karenina", "Z")
s2 = Student("Berta Novak", "Z")
s3 = Student("Cilka Celarec", "Z")
studenti = [s1, s2, s3]
predmeti = ["Uvod v racunalnistvo", "Programiranje 1", "Racunalniska arhitektura",
"Matematika", "Diskretne strukture", "Programiranje 2",
"Podatkovne baze", "Racunalniske komunikacije", "Operacijski sistemi",
"Osnove verjetnosti in statistike"]
for student in studenti:
for i in range(10):
student.oceni(choice(predmeti), randint(6, 10))Zdaj lahko izpišemo poprečne ocene vseh študentov.
for student in studenti:
print(student.ime, student.poprecje())Ana Karenina 8.142857142857142
Berta Novak 8.0
Cilka Celarec 7.571428571428571
Vse to so bile študentske zadeve. Ker je Rebeka tudi oseba, pa zna tudi pozdravljati.
rebeka.pozdravi()Pozdravljeni, jaz sem Rebeka
Še enkrat preverite in se prepričajte: metode pozdravi
nismo definirali v razredu Student, Rebeka pa vseeno
pozdravlja. Ta metoda je torej podedovana od razreda-prednika
Oseba.
Zdaj pa poglejmo definicijo metod razreda. Dodeljevanje ocene
(oceni) je trivialno. Pri računanju poprečja
(poprecje) moramo paziti le na študente, ki nimajo še
nobene ocene, zato bo vseh tam enak 0.
Zanimiv pa je konstruktor, ki mora le pripraviti slovar, v katerega
bo metoda oceni dodajala ocene. Ostalo pa prepusti
podedovanemu konstruktorju - tako da ga pokliče.
Razred Student je podedoval, recimo, metodo
pozdravi. Ko rečemo rebeka.pozdravi(), se
pokliče podedovano pozdravljanje. Student je sestavil novo
metodo poprecje in tudi tu ni dilem: ko rečemo
rebeka.poprecje(), se pokliče funkcija oceni iz razreda
Student. Metodi __init__ pa sta dve,
podedovana in nova! Pravilo je preprosto: istoimenska metoda iz
izpeljanega razreda povozi podedovano metodo.
Da je res tako, se najprej prepričajmo na preprostem zgledu: razredu
Student definirajmo novo metodo pozdravi.
class Student(Oseba):
def __init__(self, ime, spol):
super().__init__(ime, spol)
self.ocene = {}
def oceni(self, predmet, ocena):
self.ocene[predmet] = ocena
def poprecje(self):
if not self.ocene:
return 0
return sum(self.ocene.values()) / len(self.ocene)
def pozdravi(self):
print("Pozdravljeni, jaz sem {} in sem student(ka)".format(self.ime))Sestavimo novo Rebeko in jo pozovimo, naj nas pozdravi.
rebeka = Student("Rebeka", "Z")
rebeka.pozdravi()Pozdravljeni, jaz sem Rebeka in sem student(ka)
Rebeka ima tule dve metodi pozdravi, vendar se pokliče
nova in ne podedovana.
Kaj pa, če bi hoteli poklicati podedovano metodo in ne nove? Tega ne
počnemo. To je nenaravno, ne predstavljam si situacije, kjer bi nam v
lepo napisanem programu to moglo priti prav ... razen v enem primeru: ko
nova metoda kliče staro. Tule lahko razmišljamo takole:
Student je izpeljan iz Oseba in torej zna
pozdravljati. Vse, kar doda k pozdravu, je "in sem student(ka)". To
pomeni, da bi lahko najprej prepustili pozdravljanje podedovani metodi
in v Studentovem pozdravi le še dodatno
izpisali "in sem student(ka)". To se stori takole:
class Student(Oseba):
def __init__(self, ime, spol):
super().__init__(ime, spol)
self.ocene = {}
def oceni(self, predmet, ocena):
self.ocene[predmet] = ocena
def poprecje(self):
if not self.ocene:
return 0
return sum(self.ocene.values()) / len(self.ocene)
def pozdravi(self):
super().pozdravi()
print("in sem student(ka)")Rezultat sicer ni povsem enak ("in sem student(ka)" je namreč napisano v novi vrsti), a za primer bomo to vzeli v zakup.
super() je čudna funkcija, ki nam - precej
poenostavljeno - vrne self kot da bi bil objekt
razreda, ki je prednik razreda Student (torej razreda
Oseba). Torej, če rečemo self.pozdravi() se
pokliče metoda pozdravi, ki smo jo definirali v razredu
Student, saj je self objekt razreda
Student. Če pa pokličemo super().pozdravi(),
se pokliče metoda pozdravi razreda Oseba, ker
je razred Oseba prednik razreda Student. (Za
tiste, ki veste, kaj je večkratno dedovanje: funkcija super
v resnici vrača nekaj veliko veliko bolj zapletenega, saj je potrebno,
kadar dedujemo iz večih razredov, vsakič poiskati, kateri od podedovanih
razredov vsebuje funkcijo, ki jo želimo klicati. Če hočete moder nasvet:
večkratno dedovanje je gladko tlakovana široka pot v pekel. Izogibajte
se ga.)
Zdaj lahko končno pogledamo, kaj naredi konstruktor. Poleg
self dobi še dva argumenta, ime,
spol. Zanju poskrbi stari, podedovani konstruktor, zato ga
pokličemo, takole: super().__init__(ime, spol). V novem
konstruktorju je potrebno le še pripraviti slovar z ocenami.
Pa definirajmo še razred Ucitelj. Tudi ta je
Oseba (ima ime in starost in zna pozdravljati). Ena od
njegovih lastnosti je seznam predmetov, ki jih predava.
class Ucitelj(Oseba):
def __init__(self, ime, spol, predmeti):
super().__init__(ime, spol)
self.predmeti = predmeti
u1 = Ucitelj("Tomaž Poljanšek", "M", ["Programiranje 2"])Konstruktor nas, upam, ne preseneča več: ime in
spol prepusti podedovanemu konstruktorju, sam pa le še
nastavi seznam predmetov.
Sestavimo funkcijo, ki izpiše imena vseh oseb na podanem seznamu.
def izpisi_imena(s):
for e in s:
print(e.ime)Pokličemo jo lahko s seznamom študentov.
izpisi_imena(studenti)Ana Karenina
Berta Novak
Cilka Celarec
Pokličemo jo lahko tudi s seznamom, v katerem so tako učitelji kot študenti.
ljudje = [s1, s2, s3, u1]
izpisi_imena(ljudje)Ana Karenina
Berta Novak
Cilka Celarec
Tomaž Poljanšek
Pa če imamo kar nek seznam?
s = [1, 2, 3]
izpisi_imena(s)---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-16-4fcdfb3963eb> in <module>
1 s = [1, 2, 3]
----> 2 izpisi_imena(s)
<ipython-input-13-207846f9e46f> in izpisi_imena(s)
1 def izpisi_imena(s):
2 for e in s:
----> 3 print(e.ime)
AttributeError: 'int' object has no attribute 'ime'
s = ["Ana", "Berta", "Cilka"]
izpisi_imena(s)---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-17-54c61afa8ccb> in <module>
1 s = ["Ana", "Berta", "Cilka"]
----> 2 izpisi_imena(s)
<ipython-input-13-207846f9e46f> in izpisi_imena(s)
1 def izpisi_imena(s):
2 for e in s:
----> 3 print(e.ime)
AttributeError: 'str' object has no attribute 'ime'
Ne in ne. Ne s seznamom števil ne s seznamom nizov ni zadovoljna.
Čemu ne? No, saj pove: 'int' object has no attribute 'ime'.
Nekje v funkciji piše e.ime, pri čemer je e
element seznama. V prvem primeru to pomeni, da hočemo poklicati
1.ime, kar je očitno traparija. V drugem hočemo klicati,
recimo, "Ana".ime, kar prav tako ne gre. Ana sicer ni brez
vsega, ima celo kup metod - replace, upper,
endswith in še desetine drugih - a metode oz. atributa
ime ni med njimi.
Funkcija izpisi_imena ima torej določene zahteve: kot
argument ji moramo dati seznam (točneje: nekaj, prek česar je mogoče
spustiti zanko for, recimo seznam) in elementi tega seznama
morajo biti objekti, ki imajo polje ime. Z drugimi
besedami: biti morajo objekti tipa Oseba.
Res? Pravzaprav ne. Zadošča, da imajo polje ime, ni pa
treba, da so osebe.
class KrNeki:
def __init__(self):
self.ime = "jazbec"a, b, c = KrNeki(), KrNeki(), KrNeki()
a<__main__.KrNeki at 0x7f8bc08266d0>
a.ime'jazbec'
izpisi_imena([a, b, c])jazbec
jazbec
jazbec
(Tisti, ki so vajeni takoimenovanih statično tipiziranih jezikov, se
zgražajo: funkcija izpisi_imena bi morala povedati,
kakšnega tipa morajo biti objekti, ki jih sprejme. Tisti, ki imamo radi
tudi dinamično tipizirane jezike, pa razumemo, zakaj je včasih
boljše eno, včasih drugo.)
Podobno kot Python se obnašajo tudi drugi jeziki iste sorte, na primer JavaScript (kjer so reči še bolj drastične, saj imamo objektov, nimamo pa razredov!). Temu slogu (ne)preverjanja tipov pravimo duck typing: When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck. Pri tem ni pomembno, ali gre v resnici za raco ali ne; če se objekt vede kot raca, ga lahko obravnavamo kot raco.
S slednjim je povezana še ena reč, ki je v dinamičnih jezikih precej drugačna kot v statično tipiziranih. (Tisti, ki nimate pojma, o čem govorim, se boste naučili, kako je to v Pythonu in se v drugem semestu čudili Javi. Tisti, ki poznate Javo ali C++ ali C#, pa se boste zdajle čudili Pythonu.)
Recimo, da bi imel metodo naziv, ki bi vrnila naziv
osebe. Naziv osebe je lahko profesor, lahko pa je tudi
student ali studentka. Če bi imeli takšno
funkcijo, bi lahko pozdrav napisali takole.
def pozdravi(self):
print("Pozdravljeni, jaz sem {} {}".format(self.naziv(), self.ime))Namreč, pred ime smo vrinili še
self.naziv(), metodo, ki je (še?) nimamo in ki bo vrnila
naziv.
No, pa naredimo, kot smo sklenili: spremenimo metodo
pozdravi (obenem pa poenostavimo razred
Student, odvzemimo mu ocene, zato pa tudi ne potrebuje več
konstruktorja in ker v tem trenutku nima nobene metode, nekaj pa je
znotraj razreda vseeno potrebno napisati, napišimo pass).
Poglejmo, kaj se zgodi.
class Oseba:
def __init__(self, ime, spol):
self.ime = ime
self.spol = spol
def pozdravi(self):
print("Pozdravljeni, jaz sem {} {}".format(self.naziv(), self.ime))
class Student(Oseba):
passrebeka = Student("Rebeka", "Z")rebeka.ime'Rebeka'
rebeka.pozdravi()---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-25-04d7d0f7c1c2> in <module>
----> 1 rebeka.pozdravi()
<ipython-input-22-80b095f344f1> in pozdravi(self)
5
6 def pozdravi(self):
----> 7 print("Pozdravljeni, jaz sem {} {}".format(self.naziv(), self.ime))
8
9 class Student(Oseba):
AttributeError: 'Student' object has no attribute 'naziv'
Rebeko smo uspeli narediti, tudi ime ima, vendar pa javi napako, ko
ji rečemo, naj pozdravi: za to bi potrebovala metodo
naziv(), te pa ni.
Metodo naziv pa bomo naredili ločeno za študente in za
učitelje.
class Oseba:
def __init__(self, ime, spol):
self.ime = ime
self.spol = spol
def pozdravi(self):
print("Pozdravljeni, jaz sem {} {}".format(self.naziv(), self.ime))
class Student(Oseba):
def naziv(self):
if self.spol == "Ž":
return "študentka"
else:
return "študent"
class Ucitelj(Oseba):
def naziv(self):
if self.spol == "Z":
return "profesorica"
else:
return "profesor"Pa poglejmo, kako deluje.
s1 = Student("Ana Karenina", "Ž")
s1.pozdravi()Pozdravljeni, jaz sem študentka Ana Karenina
u1 = Ucitelj("Tomaz Poljanšek", "M")
u1.pozdravi()Pozdravljeni, jaz sem profesor Tomaz Poljanšek
Če pomislimo, kaj se tule v resnici dogaja, je stvar nekoliko
fascinantna. Poklicali smo metodo pozdravi. Metoda
pozdravi je podedovana iz Oseba - ne
Student ne Ucitelj nimata svoje metode
pozdravi. Izvaja se torej pozdravi, ki je
definirana v Oseba. In med izvajanjem te metode pokličemo
metodo self.naziv. V prvem primeru (Ana) je
self objekt razreda Student. Razred
Student ima metodo naziv in ko
pozdravi pokliče self.naziv se torej pokliče
Studentov naziv. V drugem primeru (Tomaz) je
self objekt razreda Ucitelj, torej je
self.naziv Uciteljev naziv.
Torej: ko pozdravi pokliče self.naziv to
včasih pomeni funkcijo naziv, ki je definirana v Student,
včasih pa funkcijo naziv, ki je definirana v
Ucitelj.
Tako kot je funkcija izpisi_imena zahtevala, da imajo
vsi objekti na seznamu ime, tako metoda
pozdravi zahteva, da ima self metodo
naziv (ki ne sprejme drugega argumenta kot
self in ki vrača nekaj, kar se da izpisati...). Odkod se ta
naziv vzame - je podedovan, je na novo definiran v
selfovem razredu ali pa je prišel v self po
kakih drugih, sumljivejših poteh - ji je vseeno. Pomembno je le, da
self.naziv obstaja.
Kar smo povedali ravnokar, je res res pomembno. To je najpomembnejša reč v objektnem programiranju, zato jo moramo še malo raziskovati.
nekdo = Oseba("Akakij Akakijevič", "M")
nekdo.pozdravi()---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-29-a47a23fbd476> in <module>
1 nekdo = Oseba("Akakij Akakijevič", "M")
2
----> 3 nekdo.pozdravi()
<ipython-input-26-f11e22da7a6f> in pozdravi(self)
5
6 def pozdravi(self):
----> 7 print("Pozdravljeni, jaz sem {} {}".format(self.naziv(), self.ime))
8
9 class Student(Oseba):
AttributeError: 'Oseba' object has no attribute 'naziv'
To smo v bistvu že videli zgoraj: študenti niso znali pozdravljati,
dokler jim nismo priskrbeli metode naziv. A to v bistvu
pomeni, da je napaka v razredu Oseba, ker zahteva nekaj,
česar (potencialno) ni.
Temu se ne reče napaka. Temu se reče abstrakten razred. V nekaterih
jezikih bi bilo potrebno "napovedati" metodo naziv kot
"čisto navidezno metodo" (pure virtual method). Razredu
Oseba bi rekli, da je abstrakten (abstract class)
in objektov tega razreda ne bi bilo mogoče sestavljati - napako bi
dobili že ob klicu Oseba("Akakij Akakijevič", "M"), češ da
takšnih, nepopolnih objektov ne sme biti.
V Pythonu in podobnih jezikih ni tako. Razred Oseba je
okrnjen, vendar bi lahko načelno imel tudi kakšne metode, ki bi normalo
delovale. Študenti bi lahko še vedno prejemali ocene, le pozdravljali ne
bi.
Nadaljujmo: dodajmo še en razred. Ali dva.
class Snazilka(Oseba):
def __init__(self, ime):
super().__init__(ime, "Ž")
class Hisnik(Oseba):
def __init__(self, ime):
super().__init__(ime, "M")
fata = Snazilka("Fata")
fata.pozdravi()---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-30-6c9c721ce1f8> in <module>
9 fata = Snazilka("Fata")
10
---> 11 fata.pozdravi()
<ipython-input-26-f11e22da7a6f> in pozdravi(self)
5
6 def pozdravi(self):
----> 7 print("Pozdravljeni, jaz sem {} {}".format(self.naziv(), self.ime))
8
9 class Student(Oseba):
AttributeError: 'Snazilka' object has no attribute 'naziv'
Da Snazilka in Hisnik ne znata
pozdravljati, je jasno in pričakovano. Ne, ker ne bi znala, le ne vesta,
kako bi se predstavila. Podobno bi se zgodilo, ko bi dodali tajnico in
osebje študentskega referata.
V vsakem jeziku in družbi pa se razvijejo norme naslavljanja
neznancev. V davnih časih moje mladosti se je neznancem uradno reklo
tovariš in tovarišica (v praksi se je to nanašalo samo na učitelje in
učiteljice), danes sta to gospod in gospa. To so privzeti nazivi,
odvisno od situacije pa uporabimo drugačnega (doktor, če gre za
zdravnika ali koga, ki se je potrudil napisati daljše besedilo, ki je
zaradi vezave v črno usnje dajalo vtis pomembnosti; docent, izredni
profesor ali profesor za nekoga, ki je naredil vse potrebne kljukice in
tako naprej). Vsi, ostali, ki niso nič posebnega, pa so torej gospodje
in gospe, zato opremimo Osebo s tem, privzetim nazivom.
class Oseba:
def __init__(self, ime, spol):
self.ime = ime
self.spol = spol
def pozdravi(self):
print("Pozdravljeni, jaz sem {} {}.".format(self.naziv(), self.ime))
def naziv(self):
if self.spol == "Ž":
return "gospa"
else:
return "gospod"
class Student(Oseba):
def naziv(self):
if self.spol == "Ž":
return "študentka"
else:
return "študent"
class Ucitelj(Oseba):
def naziv(self):
if self.spol == "Z":
return "profesorica"
else:
return "profesor"
class Snazilka(Oseba):
def __init__(self, ime):
super().__init__(ime, "Ž")
class Hisnik(Oseba):
def __init__(self, ime):
super().__init__(ime, "M")Poselimo fakulteto.
ana = Student("Ana Karenina", "Ž")
tomaz = Ucitelj("Tomaž Poljanšek", "M")
fata = Snazilka("Fata")
joze = Hisnik("Jože")Vsi so vljudni in se lepo pozdravljajo.
ana.pozdravi()Pozdravljeni, jaz sem študentka Ana Karenina.
tomaz.pozdravi()Pozdravljeni, jaz sem profesor Tomaž Poljanšek.
fata.pozdravi()Pozdravljeni, jaz sem gospa Fata.
joze.pozdravi()Pozdravljeni, jaz sem gospod Jože.
ana.pozdravi() pokliče Osebin pozdravi
(temu bomo poslej rekli Oseba.pozdravi; to v resnici
obstaja, Pythonu lahko zares napišemo Oseba.pozdravi, a to,
kar v tem primeru dobimo, je nekoliko težko razložiti). Torej
ana.pozdravi() pokliče metodo Oseba.pozdravi.
Le-ta kliče self.naziv. Ker je self isto kot
ana, ana pa je objekt tipa (ali, kot
sem govoril prej, razreda - stvar navade) Student,
self.naziv pomeni ana.naziv, torej
Student.naziv. Rezultat je niz "studentka".
dani.pozdravi() pokliče Oseba.pozdravi.
Le-ta spet kliče self.naziv. Ker je self isto
kot dani, dani pa je objekt tipa
Ucitelj, self.naziv pomeni
Ucitelj.naziv. Rezultat je niz "profesor", saj
dani.naziv() vrne "profesor".
And now for something completely different.
fata.pozdravi() pokliče Oseba.pozdravi. Le-ta
spet kliče self.naziv. Ker je self isto kot
fata, fata pa je objekt tipa
Snazilka, self.naziv pomeni
Snazilka.naziv. Tega pa ni! Pač pa ima fata
naziv, ki ga je podedovala od Oseba, namreč
Oseba.naziv. In ta pravi, da je Fata gospa.
Če želimo na tem predavanju vsaj omeniti tudi objektno programiranje na splošno, ne le v Pythonu, moramo povedati za bistveno razliko med objektnim programiranjem v Pythonu (in drugih tipičnih dinamično tipiziranih jezikih (se opravičujem za besednjak), na primer JavaScriptu) in objektnim programiranjem v statično tipiziranih jezikih, na primer C++, Javi in C#.
Metodam, kakršen je tale naziv, pravimo navidezne
metode (virtual method). Za navidezne metode je značilno,
da imajo različni podedovani razredi lahko različne metode, pri čemer pa
predniki "vedo", katero metodo poklicati. Tako
Oseba.pozdrav včasih pokliče Oseba.naziv,
včasih Student.naziv in včasih Ucitelj.naziv.
Če bi se pojavil še kak četrti, peti, ali osmi razred s svojim nazivom,
bi znal Oseba.pozdrav poklicati tudi tega.
V Pythonu izraz sam nima pomena (čemu "navidezna"?!), pa tudi
govoriti o navideznih metodah v Pythonu nima veliko smisla, saj
drugačnih metod kot navideznih v Pythonu sploh ni. V večini statično
tipiziranih jezikih pa obstajajo tudi "nenavidezne" metode. Če
naziv ne bi bil navidezna metoda, bi
Oseba.pozdrav vedno poklical Oseba.naziv,
četudi bi imel objekt, s katerim imamo opravka, svoj, "specializiran"
naziv.
Drugo, po čemer se ti jeziki razlikujejo od Pythona (in Javascripta
in podobnih), je, da bi, kot smo že omenili, zahtevali, da je
Oseba.naziv nujno definiran (točneje: deklariran), če
hočemo, da Oseba.pozdrav kliče self.naziv - ne
glede na to, ali bi bila to navidezna metoda ali ne. Včasih se zgodi
tudi, da funkcije, kakršna je Oseba.naziv, sploh ne moremo
napisati (ker bi morala delati kaj, česar se z Osebo ne da
narediti, temveč lahko to delajo šele njeni nasledniki). V tem primeru,
uh, bi definirali čisto navidezno metodo (pure virtual method),
razred Oseba bi bila abstraktni razred ... No, boljše, da
ne rinemo v to. Vedite le, da nam je v Pythonu prihranjenih veliko
komplikacij.
Na predavanjih pri Programiranju 1 objektno programiranje v Pythonu popraskamo le po površju. Veliko konceptov, kot na primer statične metode, spremenljivke razreda in podobno, ki ste jih morda srečali v drugih jezikih, pozna tudi Python. Vendar je vse to še pretežko za večino poslušalcev tega predmeta. Te stvari boste spoznavali sproti. Še več pa je tehnik, ki so specifične za Python ali pa jih poznajo le skriptni jeziki. Tudi za te pri Programiranju 1 žal ne bo časa.