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.
= Oseba("Benjamin", "M") benjamin
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.
= Student("Rebeka", "Z")
rebeka "Programiranje 1", 10)
rebeka.oceni("Uvod v racunalnistvo", 9)
rebeka.oceni( 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
= Student("Ana Karenina", "Z")
s1 = Student("Berta Novak", "Z")
s2 = Student("Cilka Celarec", "Z")
s3
= [s1, s2, s3]
studenti
= ["Uvod v racunalnistvo", "Programiranje 1", "Racunalniska arhitektura",
predmeti "Matematika", "Diskretne strukture", "Programiranje 2",
"Podatkovne baze", "Racunalniske komunikacije", "Operacijski sistemi",
"Osnove verjetnosti in statistike"]
for student in studenti:
for i in range(10):
6, 10)) student.oceni(choice(predmeti), randint(
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.
= Student("Rebeka", "Z")
rebeka 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 Student
ovem 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
= Ucitelj("Tomaž Poljanšek", "M", ["Programiranje 2"]) u1
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.
= [s1, s2, s3, u1]
ljudje izpisi_imena(ljudje)
Ana Karenina
Berta Novak
Cilka Celarec
Tomaž Poljanšek
Pa če imamo kar nek seznam?
= [1, 2, 3]
s 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'
= ["Ana", "Berta", "Cilka"]
s 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"
= KrNeki(), KrNeki(), KrNeki()
a, b, c
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):
pass
= Student("Rebeka", "Z") rebeka
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.
= Student("Ana Karenina", "Ž")
s1
s1.pozdravi()
Pozdravljeni, jaz sem študentka Ana Karenina
= Ucitelj("Tomaz Poljanšek", "M")
u1
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
Student
ov naziv. V drugem primeru (Tomaz) je
self
objekt razreda Ucitelj
, torej je
self.naziv
Ucitelj
ev 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
self
ovem 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.
= Oseba("Akakij Akakijevič", "M")
nekdo
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")
= Snazilka("Fata")
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.
= Student("Ana Karenina", "Ž")
ana = Ucitelj("Tomaž Poljanšek", "M")
tomaz = Snazilka("Fata")
fata = Hisnik("Jože") joze
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.