Seriál: V zajatí Pythona / 6. časť: Objektovo orientované programovanie

Úvod

Po­jem ob­jek­to­vo orien­to­va­né prog­ra­mo­va­nie (OOP) je hi­tom pos­led­ných ro­kov. Ob­ľú­be­ným sa stal naj­mä u ma­na­žé­rov. Tým sa za­pá­čil do ta­kej mie­ry, že ho čas­to pou­ží­va­jú bez to­ho, aby sku­toč­ne ve­de­li, o čo ide. Ak si ne­bo­daj mys­lí­te, že bu­dem pok­ra­čo­vať v du­chu „ako nau­čiť ma­na­žé­rov vý­ho­dy OOP“, mý­li­te sa. Ten­to­raz sa to­tiž po­zrie­me na OOP z prog­ra­má­tor­skej strán­ky, lep­šie po­ve­da­né, z poh­ľa­du prog­ra­má­to­ra v Pyt­ho­ne.

Pyt­ho­nov­ský OOP mo­del je zmie­ša­ni­na C++ a Mo­du­ly 3. V Pyt­ho­ne sú všet­ky at­ri­bú­ty (pre­men­né ob­jek­tu) ve­rej­né a me­tó­dy (fun­kcie ob­jek­tu) sú vir­tuál­ne. Zna­me­ná to, že ich mô­že­me ho­ci­ke­dy me­niť a ta­kis­to mô­že­me ho­ci­ke­dy pri­dá­vať no­vé. Vi­dí­me, že Pyt­hon je veľ­mi dy­na­mic­ký ja­zyk. Poj­my pub­lic, pri­va­te a pro­tec­ted, zná­me z C++, neexis­tu­jú. Je­di­ný spô­sob, ako chrá­niť dá­ta, sú pri­vát­ne pre­men­né. Tie sa však da­jú ta­kis­to me­niť (aj keď tre­ba po­ve­dať, že ich neú­my­sel­ná zme­na je sko­ro vy­lú­če­ná). Fun­kcie a me­tó­dy sa ne­da­jú pre­ťa­žiť. Ob­jek­ty ne­mu­sia ne­vyh­nut­ne pat­riť ne­ja­kej trie­de.

Toľ­ko te­da struč­né zhr­nu­tie OOP vlas­tnos­tí Pyt­ho­na. Ak pot­re­bu­je­te zá­klad­né in­for­má­cie o OOP, od­po­rú­čam kniž­ku TŘÍDY A OB­JEK­TY V C++ z vy­da­va­teľ­stva Kopp ale­bo Zá­kla­dy ob­jek­tově orien­to­va­né­ho prog­ra­mo­vá­ní z vy­da­va­teľ­stva Com­pu­ter Press, kto­rá vy­svet­ľu­je prík­la­dy v ja­zy­ku Vi­sual Ba­sic. Pub­li­ká­cia Ob­jek­to­vo orien­to­va­ná tvor­ba sys­té­mov a ja­zyk C++ z vy­da­va­teľ­stva Per­fekt pos­kyt­ne ta­kis­to množ­stvo uži­toč­ných in­for­má­cií o OOP, je však otáz­ne, či ju eš­te zo­že­nie­te.

Trie­dy

Vy­tvo­riť v Pyt­ho­ne trie­du nie je nič ná­roč­né, pre­to prík­la­dy za­čne­me zos­tra – nie­čím kom­pli­ko­va­nej­ším:

class kos:
        def __init__(self, ob­sah=None):
                self.ob­sah=ob­sah or []
        def pri­daj(self, po­loz­ka):
                "pri­da po­loz­ku do ko­sa"
                self.ob­sah.ap­pend(po­loz­ka)
        def vy­pis(self):
                "vy­pi­se ce­ly ob­sah ko­sa"
                for po­loz­ka in self.ob­sah:
                        print po­loz­ka

Ako ste si už ur­či­te všim­li, trie­da v Pyt­ho­ne sa vy­tvo­rí kľú­čo­vým slo­vom class, za kto­rým nas­le­du­je me­no trie­dy a dvoj­bod­ka. V te­le trie­dy sa na­chá­dza­jú jej at­ri­bú­ty a me­tó­dy. V prí­pa­de trie­dy kos (má to zna­me­nať kôš) som de­fi­no­val tri me­tó­dy, pri­čom ne­bo­li de­fi­no­va­né ni­ja­ké at­ri­bú­ty. Pr­vá me­tó­da __init__ je tzv. konštruk­tor. Ten sa vy­ko­ná auto­ma­tic­ky pri od­vo­de­ní in­štan­cie trie­dy. Deš­truk­tor (vy­vo­lá sa vždy pri zá­ni­ku in­štan­cie) by sme vy­tvo­ri­li me­tó­dou s me­nom __del__. Zvyš­né dve me­tó­dy trie­dy kos pri­dá­va­jú po­lož­ku do ko­ší­ka, resp. nám vy­pí­šu je­ho ob­sah. Ar­gu­ment self zod­po­ve­dá ar­gu­men­tu this z C++, po­mo­cou ne­ho pris­tu­pu­je­me k os­tat­ným at­ri­bú­tom a me­tó­dam trie­dy. Me­no self nie je zá­väz­né (skôr kon­ven­čné), na­mies­to ne­ho by sa dal pou­žiť ľu­bo­voľ­ný iný iden­ti­fi­ká­tor. Pri vo­la­ní me­tó­dy sa ar­gu­ment self (ale­bo je­ho ináč po­me­no­va­ný ek­vi­va­lent) nes­mie uvá­dzať! Toľ­ko teórie, poď­me k prak­tic­kej strán­ke ve­ci. Ak vy­tvo­rí­me in­štan­ciu trie­dy kos, mô­že­me ju za­čať ve­se­lo pou­ží­vať. In­štan­cie tried sa v Pyt­ho­ne vy­tvá­ra­jú tak­to:

>>> trash = kos() #trash je in­stan­cia trie­dy kos

Syn­tax tvor­by in­štan­cie je me­no_in­štan­cie = trie­da(). Syn­tax vo­la­nia me­tód a na ma­ni­pu­lá­ciu at­ri­bú­tov je po­dob­ný ako pri mo­du­loch: in­štan­cia.me­tó­da, resp. in­štan­cia.at­ri­bút.

>>> trash.pri­daj('pa­pier')
>>> trash.ob­sah.ap­pend('mys')
>>> trash.vy­pis()
pa­pier
mys

De­dič­nosť

Veľ­mi dô­le­ži­tý znak OOP je de­dič­nosť. Je to spô­sob, ako sa od zá­klad­nej (ro­di­čov­skej) trie­dy od­vo­dí iná, kto­rá bu­de mať at­ri­bú­ty a me­tó­dy zá­klad­nej trie­dy. Od­vo­de­nú trie­du vy­tvo­rí­me nas­le­du­jú­cou syn­taxou:

Me­noOd­vo­de­nej­Trie­dy(Me­no­Zak­lad­nej­Trie­dy):
	vy­raz1
	...
	vy­razN

Po­mo­cou vý­ra­zov v te­le od­vo­de­nej trie­dy de­fi­nu­je­me no­vé at­ri­bú­ty a me­tó­dy. V prí­pa­de, že vy­tvo­rí­me v od­vo­de­nej trie­de at­ri­bút/me­tó­du, kto­rej me­no ko­li­du­je s me­nom at­ri­bú­tu/me­tó­dy zo zá­klad­nej trie­dy, pou­ži­je sa me­no at­ri­bú­tu/me­tó­dy z od­vo­de­nej trie­dy. Vďa­ka tej­to vlas­tnos­ti zve­ľa­ďu­je­me a up­ra­vu­je­me zá­klad­né trie­dy bez nut­nos­ti vy­tvá­rať no­vé:

class kos2(kos):

        def vy­pis(self):
                "vy­pi­se pos­led­nu po­loz­ku"
                if len(self.ob­sah) > 0:
                        print self.ob­sah[len(self.ob­sah)-1]
        def vy­praz­dni(self):
                "vy­praz­dni ob­sah ko­sa"
                self.ob­sah=[]

V no­vej trie­de kos2 nám pri­bud­la me­tó­da vy­praz­dni a me­tó­da vy­pis bo­la mo­di­fi­ko­va­ná tak, aby vy­pi­so­va­la len pos­led­nú po­lož­ku z ko­ša. Konštruk­tor (__init__) a me­tó­da pri­daj bo­li zde­de­né z trie­dy kos a zos­ta­li nez­me­ne­né.

>>> trash=kos2() #trash je in­stan­cia kos2
>>> trash.pri­daj('no­vi­ny')
>>> trash.pri­daj('zo­sit')
>>> trash.vy­pis() #pos­led­na po­loz­ka
zo­sit
>>> trash.vy­praz­dni()
>>> trash.vy­pis() #kos je prazd­ny

Ak od­vo­dzu­je­me trie­du z trie­dy ne­ja­ké­ho mo­du­lu, pou­ži­je­me nas­le­du­jú­cu syn­tax:


Me­noOd­vo­de­nej­Trie­dy(Me­no­Mo­du­lu.Me­no­Zak­lad­nej­Trie­dy):
        vy­raz1
        ...
        vy­razN

Viac­ná­sob­ná de­dič­nosť

V prí­pa­de, že pot­re­bu­je­me zlú­čiť at­ri­bú­ty/me­tó­dy via­ce­rých tried na­raz, prí­de k slo­vu viac­ná­sob­ná de­dič­nosť. Syn­tax je:


Me­noOd­vo­de­nej­Trie­dy(trie­da1, trie­da2 ... trie­daN):
        vy­raz1
        ...
        vy­razN

Zro­zu­mi­teľ­ne po­ve­da­né, pos­tu­pu­je­me ako pri jed­no­du­chej de­dič­nos­ti s tým roz­die­lom, že v de­fi­ní­cii trie­dy uvá­dza­me via­ce­ro zá­klad­ných (ro­di­čov­ských) tried. Asi ste si už všim­li, že pri na­šich dvoch trie­dach (kos, kos2) má­me dve rov­no­men­né me­tó­dy vy­pis. Tu si mu­sí­me po­lo­žiť otáz­ku, kto­rá z nich sa pou­ži­je v od­vo­de­nej trie­de (ko­s3)? Jed­na to­tiž vy­pí­še ob­sah ce­lé­ho ko­ša, za­tiaľ čo dru­há len pos­led­nú po­lož­ku v ko­ši. Od­po­veď na na­šu otáz­ku znie: V prí­pa­de rov­no­men­ných at­ri­bú­tov/me­tód sa pou­ži­je at­ri­bút/me­tó­da tej trie­dy, kto­rá je v zoz­na­me ar­gu­men­tov naj­viac vľa­vo. To zna­me­ná, že v ďal­šom prí­pa­de sa vy­pí­še len pos­led­ná po­lož­ka z ko­ša (me­tó­da po­chá­dza z trie­dy kos2):

class ko­s3(kos2, kos):
        "zlep­se­ny kos"

        def vy­praz­dniP­ri­daj(self, po­loz­ka):
                "vy­praz­dni a pri­da 1 po­loz­ku do ko­sa"
                self.vy­praz­dni()
                self.pri­daj(po­loz­ka)
        def __del__(self):
                f=open('ko­s3.txt','w')
                for a in self.ob­sah:
                        f.wri­te(a+'\n')
                f.clo­se

V ďal­šej od­vo­de­nej trie­de (ko­s3) nám pri­bud­la me­tó­da vy­praz­dniP­ri­daj, kto­rá vy­práz­dni kôš a pri­dá doň jed­nu po­lož­ku. Za­ují­ma­vé na tej­to me­tó­de je, že vo­lá dve me­tó­dy zo zá­klad­nej (ro­di­čov­skej) trie­dy. Ďalej vi­dí­me v de­fi­ní­cii me­tó­du __del__, kto­rá je deš­truk­tor trie­dy ko­s3. Ten uk­la­dá ob­sah ko­ša do sú­bo­ru kos.txt pri zá­ni­ku in­štan­cie trie­dy ko­s3. Po­mo­cou už dob­re zná­me­ho at­ri­bú­tu __doc__ pris­tu­pu­je­me k do­ku­men­tač­né­mu re­ťaz­cu. Ten od­vo­de­né trie­dy ne­de­dia (čo je na­po­kon aj lo­gic­ké).

>>> trash=ko­s3()
>>> trash.vy­praz­dniP­ri­daj('zo­sit')                    
>>> trash.vy­pis()
zo­sit
>>> print trash.__doc__
vy­lep­se­ny kos

Pri­vát­ne pre­men­né

Pri­vát­ne at­ri­bú­ty a me­tó­dy sa nám ho­dia v prí­pa­de, že pra­cu­je­me s množ­stvom (od­vo­de­ných) ob­jek­tov, po­ma­ly, ale is­to sme stra­ti­li preh­ľad a hro­zí ri­zi­ko, že by sme nech­tiac moh­li mo­di­fi­ko­vať at­ri­bú­ty niek­to­rých ob­jek­tov. Pri­vát­ne pre­men­né vy­tvo­rí­me pri­po­je­ním pri­naj­men­šom dvoch pod­čiar­kov­ní­kov na za­čia­tok a maximál­ne jed­né­ho na ko­niec iden­ti­fi­ká­to­ra. Pyt­hon po­tom ut­vo­rí iden­ti­fi­ká­tor pri­vát­nej pre­men­nej pri­po­je­ním na­mi de­fi­no­va­né­ho iden­ti­fi­ká­to­ra za me­no trie­dy za­čí­na­jú­ce sa pod­čiar­kov­ní­kom. Znie to sí­ce veľ­mi kom­pli­ko­va­nie, ale v prin­cí­pe je to maximál­ne jed­no­du­ché:

>>> class trie­da:
...     __a_ = None
>>> dir(trie­da)
['__doc__', '__mo­du­le__', '_trie­da__a_'] #_trie­da__a_ je pri­vat­na pre­me­na

Za­uží­va­ný spô­sob, ako chrá­niť at­ri­bú­ty ob­jek­tov, je pou­ží­vať ne­ja­ký sys­tém tvor­by iden­ti­fi­ká­to­rov, napr. za­čať kaž­dé slo­vo v iden­ti­fi­ká­to­re veľ­kým pís­me­nom (Pyt­hon­Je­Su­per) ale­bo od­de­ľo­vať pod­čiar­kov­ní­kom (vy­pis_ce­ly_ob­sah) atď. Zá­le­ží len na vás, aký spô­sob si vy­be­rie­te. Dô­le­ži­té je, aby ste ho aj sku­toč­ne dodr­žia­va­li.

„Štruk­tú­ry“

Pyt­hon ne­po­nú­ka vlast­ný dá­to­vý typ pre štruk­tú­ry, ako to ro­bí napr. C ale­bo Pas­cal. Pre­to ak chce­me vy­tvo­riť štruk­tú­ru, po­mô­že­me si trie­da­mi:

>>> class auto:
...     pass
... 
>>> audi=auto() #pot­re­bu­je in­stan­ciu au­ta
>>> audi.far­ba='cer­ve­na'
>>> audi.ver­zia='a4'
>>> dir(audi)
['far­ba', 'ver­zia']

Vý­nim­ky

Po­mo­cou tried si mô­že­me de­fi­no­vať vlas­tné vý­nim­ky. Opäť nej­de o nič kom­pli­ko­va­né. V na­šom prík­la­de tes­tu­je­me prí­ka­zom if, či je pres­ne 12 ho­dín. Spl­ní sa pod­mien­ka, vy­vo­lá sa vý­nim­ka (vý­zva na obed).

im­port ti­me #mo­dul na pra­cu s ca­som

class ca­sO­be­da:
        def __init__(self, hod­no­ta):
                self.hod­no­ta = hod­no­ta
        def __str__(self):
                re­turn 'Ide sa obe­do­vat, je  ' + `self.hod­no­ta` + ' ho­din!'

if ti­me.gmti­me(ti­me.ti­me())[3]+1 == 12: #je 12 ho­din?
        rai­se ca­sO­be­da, ti­me.gmti­me(ti­me.ti­me())[3]+1

Eš­te mi zos­tá­va spo­me­núť, že at­ri­bút __str__ ob­sa­hu­je re­ťa­zec s chy­bo­vým hlá­se­ním, kto­ré sa vy­pí­še pri vy­vo­la­ní vý­nim­ky vý­ra­zom rai­se. Aby moh­la byť čí­sel­ná hod­no­ta v at­ri­bú­te self.hod­no­ta spo­je­ná s zvyš­ným re­ťaz­com, mu­sí­me ju uviesť v ob­rá­te­ných úvo­dzov­kách (fun­kcia str(self.hod­no­ta) by spô­so­bi­la to is­té).

Zá­ver

Po šies­tich pok­ra­čo­va­niach sme sa dos­ta­li ku kon­cu se­riá­lu. Ma­li by ste už byť schop­ní na­pí­sať jed­no­duch­šie prog­ra­my v Pyt­ho­ne. Ak vám to stá­le nes­ta­čí a vy chce­te zlep­šiť úro­veň svo­jich ve­do­mos­tí, od­po­rú­čam preš­tu­do­vať do­ku­men­tá­ciu do­dá­va­nú k Pyt­ho­nu. Tá sa dá stiah­nuť aj zo strán­ky www.pyt­hon.org, kde náj­de­te aj nie­koľ­ko mi­mo­riad­ne dob­rých HOW-TO do­ku­men­tov (napr. je tam exce­len­tne vy­svet­le­ná prá­ca s re­gu­lár­ny­mi vý­raz­mi). Dob­rým zoz­na­mom lin­kov pre Pyt­hon je aj na star­ship.pyt­hon.net. Lin­ky na pyt­ho­nov­ské prog­ra­my sú dos­ptup­né z www.vex.net/par­nas­sus. Tých­to nie­koľ­ko strá­nok, kto­ré sú, sa­moz­rej­me, v an­glič­ti­ne, ob­sa­hu­je lin­ky na tak­mer všet­ky dô­le­ži­té ve­ci o Pyt­ho­ne. Ke­by ste chce­li rad­šej nie­čo v na­šej ma­ter­či­ne, net­re­ba zú­fať. V prí­pa­de, že všet­ko pôj­de dob­re, za­čiat­kom no­vé­ho ro­ku zreali­zu­jem por­tál, kto­rý bu­de pri­ná­šať no­vin­ky o Pyt­ho­ne, bu­de ob­sa­ho­vať dô­le­ži­té lin­ky, ti­py a tri­ky, HTML ver­ziu se­riá­lu atď. Zos­tá­va eš­te uviesť ad­re­su por­tá­lu: pyt­hon.co­ders.sk.


Ďal­šie čas­ti >>

Zdroj: PC Revue



Ohodnoťte článok:
   
 
 
  Zdieľaj cez Facebook Zdieľaj cez Google+ Zdieľaj cez Twitter Zdieľaj cez LinkedIn Správy z RSS Správy na smartfóne Správy cez newsletter