Python


Úvod

Pojem "objektovo orientované programovanie" (OOP) je hitom posledných rokov. Obľúbený sa stal najmä u manažérov. Tým sa zapáčil do takej miery, že ho často používaju bez toho, aby skutočne vedeli, o čo ide. Ak si nebodaj myslíte, že budem pokračovať v duchu "ako naučiť manažérov výhody OOP", namáte pravdu. Dnes sa totiž pozrieme na OOP z programátorskej stránky, lepšie povedané z pohľadu programátora v Pythone.

Pythonovský OOP model je zmiešanina z C++ a Moduly 3. V Pythone sú všetky atribúty (premmené objetku) verejné a metódy (funkcie objektu) sú virtuálne. Znamená to, že ich môžeme hocikedy meniť a takisto môžeme hocikedy pridávať nové. Vidíme, že Python je veľmi dynamický jazyk. Pojmy "public", "private" a "protected", známe z C++ neexistujú. Jediný spôsob, ako chrániť dáta sú privátne premenné. Tie sa však dajú tiež meniť (aj keď treba povedať, že ich neúsmyselná zmena je skoro vylúčená). Funkcie a metódy sa nedajú preťažiť. Objekty nemusia nevyhnutne patriť nejakej triede.

Toľko teda stručné zhrunutie OOP vlastností Pythona. Ak potrebujete základné informácie o OOP, odporúčam knížku: "TŘÍDY A OBJEKTY V C++" z vydavateľstva Kopp alebo "Základy objektově orientovaného programovaní" z vydavateľsva Computer Press, ktorá vysvetľuje príklady v jazyku Visual Basic. Knižka "Objektovo-orientovaná tvorba systémov a jazyk C++" z vydavateľsta Perfekt posktytne takisto množstvo užitočných informácii o OOP, je však otázne, či ju ešte budete môcť zohnať.

Triedy

Vytvoriť v Pythone triedu nie je nič náročné, preto začneme príklady zostra niečím trošku komplikovanejším:
class kos:
        def __init__(self, obsah=None):
                self.obsah=obsah or []
        def pridaj(self, polozka):
                "prida polozku do kosa"
                self.obsah.append(polozka)
        def vypis(self):
                "vypise cely obsah kosa"
                for polozka in self.obsah:
                        print polozka
Ako ste si už určite všimli, trieda v Pythone sa vytvorí kľúčovým slovom class, za ktorým nasleduje meno triedy a dvojbodka. V tele triedy sa nachádzajú jej atribúty a metódy. V prípade triedy kos (má to znamenať kôš) som definoval 3 metódy, pričom neboli definované žiadne atribúty. Prvá metóda __init__ je tzv. konštruktor. Ten sa vykoná automaticky pri odvodení inštancie triedy. Deštruktor (vyvolá sa vždy pri zániku inštancie) by sme vytvorili metódou s menom __del__. Zvyšné 2 metódy tiedy kos pridávajú položku do košika, resp. nám vypíšu jeho obsah. Argument self zodpovedá argumentu this z C++, pomocou neho pristupujeme k ostatným atribútom a metódam triedy. Meno self nie je záväzné (skôr konvenčné), namiesto neho by sa dal použiť ľubovoľný iný identifikátor. Pri volaní metódy sa argument self (alebo jeho ináč pomenovaný ekvivalent) nesmie uvádzať! Toľko teórie, poďme k praktickej stránke veci. Ak vytvoríme inštanciu triedy kos, môžeme ju začať veselo pužívať. Inštancie tried sa v Pythone vytvárajú nasledovne:
>>> trash = kos() #trash je instancia triedy kos
Syntax tvorby inštancie je meno_inštancie = trieda(). Syntax volania metód a na manipuláciu atribútov je podobný ako u modulov: inštancia.metóda resp. inštancia.atribút.
>>> trash.pridaj('papier')
>>> trash.obsah.append('mys')
>>> trash.vypis()
papier
mys

Dedičnosť

Veľmi doležitým znakom OOP je dedičnosť. Je to spôsob, ako sa od základnej (rodičovskej) triedy odvodí iná, ktorá bude mať atribúty a metódy základnej triedy. Odvodenú triedu vytvoríme nasledovným syntaxom:
MenoOdvodenejTriedy(MenoZakladnejTriedy):
    vyraz1
    ...
    vyrazN

Pomocou výrazov v tele odvodenej triedy definujeme nové atribúty a metódy. V prípade, že vytvoríme v odvodenej triede atribút/metódu, ktorej meno koliduje s menom atribútu/metódy zo základnej triedy, použije sa meno atribútu/metódy z odvodenej triedy. Vďaka tejto vlastnosti zveľaďujeme a upravujeme základné triedy bez nutnosti vytvárať nové:
class kos2(kos):

        def vypis(self):
                "vypise poslednu polozku"
                if len(self.obsah) > 0:
                        print self.obsah[len(self.obsah)-1]
        def vyprazdni(self):
                "vyprazdni obsah kosa"
                self.obsah=[]
V novej triede kos2 nám pribudla metóda vyprazdni a metóda vypis bola modifikovaná tak, aby vypisovala len poslednú položku z koša. Konštruktor (__init__) a metóda pridaj boli zdedené z triedy kos a zostali nezmenené.
>>> trash=kos2() #trash je instancia kos2
>>> trash.pridaj('noviny')
>>> trash.pridaj('zosit')
>>> trash.vypis() #posledna polozka
zosit
>>> trash.vyprazdni()
>>> trash.vypis() #kos je prazdny
Ak odvodzujeme triedu z triedy nejakého modulu, použijeme nasledovný syntax:
MenoOdvodenejTriedy(MenoModulu.MenoZakladnejTriedy):
        vyraz1
        ...
        vyrazN

Viacnásobná dedičnosť

V prípade, že potrebujeme zlúčiť atribúty/metódy viacerých tried naraz, príde k slovu viacnásobná dedičnosť. Syntax je:
MenoOdvodenejTriedy(trieda1, trieda2 ... triedaN):
        vyraz1
        ...
        vyrazN

Zrozumiteľne povedané, postupujeme ako pri jednoduchej dedičnosti s tým rozdielom, že v definícii triedy uvádzame viacero základných (rodičovských) tried. Asi ste si už všimli, že u našich dvoch tried (kos, kos2) máme dve rovnomenné metódy vypis. Tu si musíme položiť otázku, ktorá z nich sa použije v odvodenej triede (kos3)? Jedna totiž vypíše obsah celého koša, zatiaľ čo druhá len poslednú položku v koši. Odpoveď na našu otázku znie: V prípade rovnomenných atribútov/metód sa použije atribút/metóda tej triedy, ktorá je v zozname argumentov najviac vľavo. To znamená, že v nižšie uvedenom prípade sa vypíše len posledná položka z koša (metóda pocházda z triedy kos2):
class kos3(kos2, kos):
        "vylepseny kos"

        def vyprazdniPridaj(self, polozka):
                "vyprazdni a prida 1 polozku do kosa"
                self.vyprazdni()
                self.pridaj(polozka)
        def __del__(self):
                f=open('kos3.txt','w')
                for a in self.obsah:
                        f.write(a+'\n')
                f.close
V ďaľšej odvodenej triede (kos3) nám pribudla metóda vyprazdniPridaj, ktorá vyprázdni kôš a pridá doň jednu položku. Zaujímavé na tejto metóde je, že volá dve metódy zo základnej (rodičovskej) triedy. Ďalej vidíme v definícii metódu __del__, ktorá je deštruktor triedy kos3. Ten ukladá obsah koša do súboru kos.txt pri zániku inštancie triedy kos3. Pomocou už dobre známeho atribútu __doc__ pristupujeme k dokumentačnému reťazcu. Ten odvodené triedy nededia (čo je aj napokon aj logické).
>>> trash=kos3()
>>> trash.vyprazdniPridaj('zosit')
>>> trash.vypis()
zosit
>>> print trash.__doc__
vylepseny kos

Privátne premenné

Privátne atribúty a metódy sa nám hodia v prípade, že pracujeme s množstvom (odvodených) objektov, pomaly ale isto sme stratili prehľad a hrozí riziko, že by sme nechtiac mohli modifikovať atribúty niektorých objektov. Privátne premenné vytvoríme pripojením prinajmenšom dvoch podtržítiek na začiatok a maximálne jedného na koniec identifikátora. Python potom utvorí identifikátor privátnej premennéj pripojením nami definovaného identifikátora za meno triedy začínajúce podtržítkom. Znie to síce veľmi komplikovanie, ale v princípe je to maximálne jednoduché:
>>> class trieda:
...     __a_ = None
>>> dir(trieda)
['__doc__', '__module__', '_trieda__a_'] #_trieda__a_ je privatna premmena
Zaužívaným spôsobom, ako chrániť atribúty objektov, je používať nejaký systém tvorby identifikátorov. Napr. začať každé slovo v identifikátore veľkým písmenom (PythonJeSuper) alebo oddeľovať podržítkom (vypis_cely_obsah) atď. Záleži len na vás, aký spôsob si vyberiete. Dôležité je, aby ste ho aj skutočne doržiavali.

"Štruktúry"

Python neponúka vlastný dátový typ pre štruktúry, ako to robí napr. C alebo Pascal. Preto ak chceme vytvoriť štruktúru, pomôžeme si triedami:
>>> class auto:
...     pass
...
>>> audi=auto() #potrebuje instanciu auta
>>> audi.farba='cervena'
>>> audi.verzia='a4'
>>> dir(audi)
['farba', 'verzia']

Výnimky

Pomocou tried si môžme definovať vlastné výnimky. Opäť sa nejedná o nič komplikované. V našom príklade testujeme príkazom if, či je presne 12 hodin. Splní sa podmienka, vyvolá sa výnimka (výzva na obed).
import time #modul na pracu s casom

class casObeda:
        def __init__(self, hodnota):
                self.hodnota = hodnota
        def __str__(self):
                return 'Ide sa obedovat, je  ' + `self.hodnota` + ' hodin!'

if time.gmtime(time.time())[3]+1 == 12: #je 12 hodin?
        raise casObeda, time.gmtime(time.time())[3]+1
Ešte mi zostáva spomenúť, že atribút __str__ obsahuje reťazec s chybovou hláškou, ktorá sa vypíše pri vyvolaní výnimky výrazom raise. Aby mohla byť číselná hodnota v atribúte self.hodnota spojená s zvyšným reťazcom, musíme ju uviesť v obrátených úvodzovkách (funkcia str(self.hodnota) by spôsobila to isté).

Želám vám veľa úspešných programov v Pythone.

Martin Uzak

[Pridať príspevok k článku][Verzia pre tlač]