Níže uvedený text pochází z prvního vydání. Nad tímto textem se nachází aktuální stav po revizi sm>řující k druhému vydání.
Nyní se pustíme do n>čeho, co se do doby asi před p>ti lety považovalo za náročné téma. V současnosti se již objektov> orientované programování stalo normou. Jazyky, jako jsou Java a Python zt>lesňují tento koncept do té míry, že se setkání s objekty nevyhnete již při programování jednoduchých v>cí. Takže o čem vlastn> objektové programování pojednává?
Podle mého názoru k nejlepším úvodům do problematiky patří:
Poznámka překladatele: Není mi známo, že by uvedená literatura byla přeložena do českého jazyka. Pokud je skutečnost jiná, dejte mi, prosím, v>d>t.
Knihy jsou uvedeny v pořadí rostoucí hloubky, velikosti a akademické exaktnosti. V>tšin> neprofesionálních programátorů bude vyhovovat první kniha. Úvod, který je více zam>řen na programování, naleznete v Object Oriented Programming autora jménem Timothy Budd (druhé vydání). Osobn> jsem tuto knihu nečetl, ale op>vují ji recenze lidí, jejichž názorů si vážím.
A konečn> celou hromadu informací o všech možných tématech kolem objektov> orientovaného programování (OOP) naleznete na webových stránkách http://www.cetus-links.org.
Protože předpokládám, že teď nemáte čas ani sklony k tomu, abyste zkoumali obsah všech uvedených knih a odkazů, předložím vám stručný přehled tohoto konceptu. (Poznámka: N>kterým lidem se koncept objektové orientace zdá t>žce pochopitelný, jiným sedne hned. Pokud patříte k té první kategorii, netrapte se tím. Objekty můžete docela dobře používat i v případ>, že vám jejich výhody nejsou zcela zřejmé.)
A ješt> jedna poznámka na záv>r. V této části budeme používat pouze Python, protože ani BASIC, ani Tcl objekty nepodporují. Při dodržování určitých konvencí zápisu kódu lze koncept objektov> orientovaného návrhu využít i v jazycích, které nejsou objektov> orientované, ale v takovém případ> jde spíše jen o možné východisko z nouze než o doporučovanou strategii. Pokud je váš problém výhodn> řešitelný technikami objektov> orientovaného návrhu a programování, pak je vždy nejlepší, když použijete objektov> orientovaný jazyk.
Objekty v sob> zahrnují nejen data ale i funkce, které nad uvedenými daty pracují. Data i funkce jsou svázány dohromady takovým způsobem, že objekt můžete předat z jedné části programu do druhé a ob> části mohou přistupovat nejen k datovým atributům, ale přístupné jsou i operace.
Takže například objekt typu řet>zec (string) poskytuje nejen prostor pro uložení znaků řet>zce, ale poskytuje i metody pro provád>ní operací nad uloženým řet>zcem — vyhledávání, zm>nu malých písmen na velká, určení délky řet>zce a podobn>.
V souvislosti s objekty se hovoří o komunikaci zasíláním zpráv. Jeden objekt zašle jinému objektu zprávu a přijímající objekt na ni zareaguje provedením jedné ze svých operací, takzvané metody. Takže říkáme, že metoda je vlastním objektem vyvolána při příjmu odpovídající zprávy. Způsob zápisu tohoto obratu bývá různý, ale nejb>žn>jší z nich se snaží napodobit přístup ke složkám záznamu — používá tečkové notace. Takže pro třídu fiktivního prvku (widget[1]) můžeme psát:
w = Widget() # vytvoř novou instanci w třídy Widget() w.paint() # zašli mu zprávu 'paint' (tj. 'vykresli')
Tento zápis způsobí, že bude vyvolána metoda paint().
Poznámka překladatele: Připomeňme si znovu, že je to vnitřní funkce objektu. Zatímco v neobjektových jazycích (například Pascal, C) se tento zápis používal pouze pro zpřístupn>ní datových složek záznamu, u objektov> orientovaných jazyků se používá jak pro zpřístupn>ní datových složek objektu (v tomto smyslu je objekt totéž, co v neobjektových jazycích záznam), tak pro zpřístupn>ní jeho vnitřních funkcí (říká se jim také členské funkce, protože jsou členy objektu nebo třídy). Ale nejpoužívan>jším a obecn> srozumitelným pojmem pro takové funkce je metoda. To, že se nejedná o datovou složku záznamu (nebo objektu) se v různých jazycích obvykle vyjadřuje tím, že zápis připomíná volání funkce. Typicky se za identifikátor metody zapisují kulaté závorky. V nich se mohou uvád>t i požadované argumenty — jako u funkcí.
Objekty mohou být různého typu ve stejném smyslu, jako mohou být i data různého typu. Množina objektů se shodnými charakteristikami je známa pod pojmem třída. Třídu můžeme nadefinovat a potom můžeme vytvářet instance této třídy, což jsou vlastn> skutečné objekty. Odkazy (reference) na tyto objekty můžeme v našem programu ukládat do prom>nných.
Podívejme se na konkrétní příklad a uvidíme, jestli se to podaří
vysv>tlit lépe. Vytvoříme třídu Zprava, která bude popisovat
existenci datové složky typu řet>zec — tj. textu zprávy — a
metodu k vytišt>ní (zobrazení) zprávy (vytisknout).
class Zprava:
def __init__(self, retezec):
self.text = retezec
def vytisknout(self):
print self.text
Poznámka 1: Jedna z metod této třídy se nazývá
__init__. Jde o speciální metodu, které se říká
konstruktor. Říká se jí tak, protože se volá v okamžiku vytváření
(konstruování) nové instance objektu. Pokud uvnitř této metody n>jaké
prom>nné n>co přiřadíme (což v jazyce Python zajistí její vytvoření), pak
bude tato prom>nná patřit výhradn> nové instanci. V jazyce Python existuje
řada podobných metod. Tém>ř všechny podobné jsou od zbytku odlišeny tím, že
používají speciální tvar jména __xxx__ (tedy dva znaky podtržení, slovo a op>t dva
znaky podtržení).
Poznámka 2: Ob> uvedené metody používají první parametr
self. (Při
personifikaci objektu by se to dalo přeložit jako já nebo já
sám.) Toto pojmenování je dáno pouze konvencí, ale vhodn>
vyjadřuje výskyt (existenci) objektu. Jak uvidíme pozd>ji, tento parametr
bude napln>n až za b>hu, a to interpretem — nikoliv tedy
programátorem. To jinými slovy znamená, že metoda vytiskni bude
volána bez zadávání argumentů takto: m.vytiskni().
Poznámka překladatele: Teoreticky bychom tomuto parametru mohli přid>lit jakékoliv jméno, ale řada pomocných nástrojů předpokládá dodržování této konvence. Navíc tomu všichni uživatelé jazyka Python rozumí na první pohled. Pokud nejde o pouhé pokusy, nepoužívejte jiné jméno prvního parametru. Dodržujte uvedenou konvenci i vy.
Poznámka 3: Uvedenou třídu jsme pojmenovali Zprava s
velkým 'Z'. Jde op>t pouze o konvenci, která se ovšem používá velmi často, a
to nejen v jazyce Python, ale i v jiných objektov> orientovaných jazycích.
Daná konvence říká, že jména metod by m>la začínat malým písmenem a další
slova, ze kterých se jméno metody skládá, by m>la začínat velkým písmenem.
Takže například metody "vypočítej stav účtu" bychom zapsali:
vypocitejStavUctu.
Poznámka překladatele: pojmy třída a instance třídy (tedy objekt) jsou velmi důležité. Bez jejich dokonalého pochopení budete v problematice objektov> orientovaného programování jen tápat. Pokud se považujete za laiky, mohl by vám v>c osv>tlit výklad t>chto pojmů napasovaný na Pohádku o Popelce[2].
V tomto okamžiku se možná budete chtít znovu podívat na kapitolu Data, datové typy a prom>nné a zopakovat si uživatelsky definované typy. Příklad s adresou by m>l být nyní o n>co jasn>jší.
Třída je v jazyce Python v podstat> jediným uživatelsky
definovaným typem. Třída, která má pouze atributy (datové složky), ale žádné metody (s
výjimkou __init__), odpovídá záznamům v jazyce BASIC a v jiných neobjektových
jazycích.
Teď, když už máme definovánu třídu Zprava, můžeme vytvářet
její instance a můžeme s nimi manipulovat:
z1 = Zprava("Ahoj lidi!")
z2 = Zprava("Sbohem. Bylo to krátké, ale sladké.")
poznamky = [z1, z2] # vlož objekty do seznamu
for zpr in poznamky:
zpr.vytisknout() # každou zprávu vytiskni
Takže vidíme, že s třídou zacházíme, jako kdyby to byl standardní datový typ jazyka Python. A to byl vlastn> cíl tohoto cvičení.
Zatím tedy umíme definovat své vlastní typy (třídy), umíme vytvářet jejich instance a umíme je přiřazovat do prom>nných. T>mto objektům (instancím) pak můžeme zasílat zprávy, což způsobí provedení metod, které jsme definovali. Ale co se týká objektov> orientovaného přísupu, je zde ješt> jedna v>c. Z mnoha pohledů jde o nejdůležit>jší vlastnost vůbec.
M>jme dva objekty různých tříd, které podporují stejnou množinu zpráv, ale definují své vlastní odpovídající metody. V takovém případ> můžeme tyto objekty udržovat ve společné kolekci a v našem programu s nimi můžeme zacházet stejným způsobem. Objekty se však budou chovat každý jinak (po svém). Této schopnosti — chovat se jinak při zpracování stejné zprávy — se říká polymorfismus (doslova mnohotvárnost, ale nepřekládá se).
Typicky se toho využívá například při existenci n>kolika různých
grfických objektů, které se umí vykreslit, když obdrží zprávu 'paint'.
Objekt kruhu vykreslí ve srovnání s objektem trojúhelníku velmi odlišný
obrazec, ale pokud oba definují metodu paint, můžeme tento
rozdíl jako programátoři ignorovat a můžeme o nich uvažovat jako o
tvarech.
Podívejme se na příklad, kde místo vykreslování tvarů budeme vypočítávat
jejich plochy. Nejdříve vytvoříme třídy Ctverec a
Kruh:
class Ctverec:
def __init__(self, strana):
self.strana = strana
def vypocitejPlochu(self):
return self.strana**2
class Kruh:
def __init__(self, polomer):
self.polomer = polomer
def vypocitejPlochu(self):
import math
return math.pi*(self.polomer**2)
Nyní vytvoříme seznam tvarů (buď kruhů nebo čtverců) a poté vytiskneme jejich plochy:
seznam = [Kruh(5), Kruh(7), Ctverec(9), Kruh(3), Ctverec(12)]
for tvar in seznam:
print "Plocha je: ", tvar.vypocitejPlochu()
Pokud nyní zkombinujeme uvedené myšlenky s moduly, dostaneme velmi mocný
mechanismus pro opakované použití kódu. Uložme definice tříd do modulu
— řekn>me tvary.py — a když potom budeme chtít
manipulovat s tvary, jednoduše nejdříve provedeme import tohoto modulu.
Přesn> takto je do systému Python zařazena řada standardních modulů. To je
důvod, proč se přístup k metodám objektu tolik podobá používání funkcí z
modulu.
D>dičnost se často používá jako mechanismus pro implementování polymorfismu. V mnoha objektov> orientovaných jazycích je to ve skutečnosti jediný způsob pro implementování polymorfismu. Funguje to následovn>.
Třída může z rodičovské třídy nebo také nadtřídy (super class) d>dit jak atributy (datové prvky) tak operace. To znamená, že nová třída, která se ve v>tšin> v>cí podobá jiné tříd>, nemusí znovu implementovat všechy metody existující třídy. Místo toho může její schopnosti zd>dit a předefinovat (override) jen to, co se má d>lat jinak (například metodu pro vykreslování, o které jsme se zmínili ve výše uvedeném případ>).
Nejlepší bude ukázat vše na příkladu. Vytvoříme hierarchii tříd pro bankovní účty, u kterých můžeme ukládat hotovost, zjišťovat stav účtu a realizovat výb>r. N>které z účtů poskytují úroky (pro naše účely budeme předpokládat, že úrok je vypočítán při každém vkladu — zajímavá inovace pro bankovní sv>t) a u jiných se při výb>ru účtuje poplatek.
Podívejme se, jak by to mohlo vypadat. Nejdříve uvažme atributy a operace bankovního účtu na nejobecn>jší (nebo abstraktní) úrovni.
Obvykle je nejlepší uvažovat nejdříve o operacích a teprve podle potřeby doplnit atributy tak, abychom mohli oprerace realizovat. Takže u bankovního účtu můžeme:
Pro tyto operace potřebujeme znát číslo bankovního účtu (pro operaci převodu) a současný stav účtu. Vytvořme odpovídající třídu:
class ChybaZustatku(Exception): pass
class BankovniUcet:
def __init__(self, pocatecniVklad):
self.stav = pocatecniVklad
print "Byl založen účet s počátečním stavem %5.2f korun." % self.stav
def vlozit(self, castka):
self.stav = self.stav + castka
def vybrat(self, castka):
if self.stav >= castka:
self.stav = self.stav - castka
else:
raise ChybaZustatku("Litujeme. Na vašem účtu je jen %6.2f korun."
% self.stav)
def zustatek(self):
return self.stav
def prevod(self, castka, ucet):
try:
self.vybrat(castka)
ucet.vlozit(castka)
except ChybaZustatku, e:
print str(e)
Poznámka 1: Před výb>rem z účtu kontrolujeme stav účtu a pro
ošetření chyb používáme výjimky. Chyba ChybaZustatku samozřejm>
neexistuje, takže si ji musíme vytvořit. Definujeme ji
jako třídu, která je odvozena od zabudované třídy Exception. Ta
je bázovou třídou všech zabudovaných výjimek systému Python a m>la by se
používat pro všechny uživatelsky definované výjimky. Při vytváření instance
této třídy (příkazem raise) lze předat řet>zec, který lze z
objektu výjimky extrahovat zabudovanou funkcí str()..
Poznámka překladatele: V nov>jších verzích jazyka
Python se pro výjimky vždy doporučuje používat třídy, které odvodíme od
bázové třídy Except. V roli instancí výjimek se již
nedoporučuje používat řet>zce.
Poznámka 2: Metoda prevod používá pro realizaci
převodu členské funkce neboli metody vybrat a
vlozit třídy BankovniUcet. Tento přístup je při
objektov> orientovaném programování velmi b>žný a je znám jako zasílání
zpráv sob> samému (self messaging). Znamená to, že odvozené
třídy mohou implementovat své vlastní verze metod vlozit a
vybrat, přičemž metoda prevod může u všech typů
účtů zůstat stejná.
Teď za použití d>dičnosti vytvoříme účet, na který budou připisována
procenta (budeme předpokládat 3 %) při každém vkladu. Třída se bude
shodovat s dříve uvedenou třídou BankovniUcet s výjimkou metody
vlozit. Jednoduše ji předefinujeme (override):
class UrocenyUcet(BankovniUcet):
def vlozit(self, castka):
BankovniUcet.vlozit(self, castka)
self.stav = self.stav * 1.03
A je to! Začíná se ukazovat síla objektov> orientovaného programování.
Všechny ostatní metody byly zd>d>ny z třídy BankovniUcet (tím,
že jsme BankovniUcet uvedli do závorek za jméno nové třídy).
Povšimn>te si, že metoda vlozit místo kopírování kódu rad>ji
volá metodu vlozit své nadtřídy (superclass). Takže
pokud nyní upravíme metodu vlozit třídy
BankovniUcet například přidáním n>jakých kontrol chyb, projeví
se tyto zm>ny automaticky i v podtříd>.
Poznámka překladatele: Místo pojmu nadtřída (super class) se často používá pojem bázová třída (base class) a místo pojmu podtřída (sub-class) se často používá pojem odvozená třída (derrived class).
Tento typ účtu se op>t shoduje s třídou BankovniUcet pro
standardní bankovní účet s tím rozdílem, že se tentokrát při každém výb>ru
účtují tři koruny. Stejn> jako v případ> třídy UrocenyUcet
můžeme novou třídu vytvořit d>d>ním z třídy BankovniUcet a
úpravou metody vybrat.
class UcetSPoplatkem(BankovniUcet):
def __init__(self, pocatecniVklad):
BankovniUcet.__init__(self, pocatecniVklad)
self.poplatek = 3
def vybrat(self, castka):
BankovniUcet.vybrat(self, castka + self.poplatek)
Poznámka 1: Velikost poplatku je uložena v členské
prom>nné, takže ji podle potřeby můžeme pozd>ji m>nit. Povšimn>te si,
že zd>d>nou metodu __init__ můžeme volat stejn> jako každou
jinou metodu.
Poznámka 2: Poplatek jednoduše přičítáme k požadované hodnot>
výb>ru a provedení celé operace zajistíme voláním metody vybrat
třídy BankovniUcet.
Poznámka 3: Vedlejším efektem tohoto postupu je to, že se poplatek uplatní i při převodu na jiný účet. Pravd>podobn> to takto chceme, takže je to v pořádku.
Abychom si vyzkoušeli, že to všechno funguje, zkuste spustit následující
kus kódu (buď z příkazového řádku systému Python nebo vytvořením souboru s
t>mito testy). Následující kód předpokládá, že jste výše uvedené definice tříd uložili do
souboru bankovniucet.py. Pokud byste je uložili do jiného
souboru, musíte zm>nit jméno modulu v prvním řádku souboru.
from bankovniucet import * # Nejdříve vyzkoušíme standardní BankovniUcet. a = BankovniUcet(500) b = BankovniUcet(200) a.vybrat(100) # a.vybrat(1000) a.prevod(100, b) print "A = ", a.zustatek() print "B = ", b.zustatek() # Teď vyzkoušíme UrocenyUcet. c = UrocenyUcet(1000) c.vlozit(100) print "C = ", c.zustatek() # A ješt> UcetSPoplatkem. d = UcetSPoplatkem(300) d.vlozit(200) print "D = ", d.zustatek() d.vybrat(50) print "D = ", d.zustatek() d.prevod(100, a) print "A = ", a.zustatek() print "D = ", d.zustatek() # A nakonec provedeme převod z účtu s poplatky na úročený účet. # Z účtu s poplatky by se m>l odečíst i poplatek a na úročeném # účtu by m>l přibýt i úrok. print "C = ", c.zustatek() print "D = ", d.zustatek() d.prevod(20, c) print "C = ", c.zustatek() print "D = ", d.zustatek()
Nyní odkomentujte řádek a.vybrat(1000) a uvidíte, jak
zafunguje výjimka.
A je to. Jde o pom>rn> zjednodušený příklad, ale ukazuje, jak můžeme využít d>dičnosti k rychlému rozšíření existující funkčnosti o nové rysy.
Ukázali jsme si, jak lze příklad vytvořit po etapách a jak lze vytvořit testovací program, kterým funkčnost řešení ov>říme. Naše testy nebyly úplné v tom smyslu, že jsme nepokryli všechny možné případy. Mohli bychom přidat také další kontroly. Co kdyby byl například vytvořen účet se záporným zůstatkem…
Jedna z otázek, která vás mohla napadnout, zní: Jak bychom m>li zacházet s v>tším množstvím objektů? Nebo také: Jak bychom m>li zacházet s objekty, které vytvoříme za b>hu programu? Statické vytvoření objektů bankovních účtů — jak jsme to učnili výše — je velmi snadné:
ucet1 = BankovniUcet(...) ucet2 = BankovniUcet(...) ucet3 = BankovniUcet(...) atd.
Ale u reálných řešení dopředu nevíme kolik účtů budeme chtít vytvořit. Jak si s tím poradíme? Uvažujeme nyní o problému trochu podrobn>ji.
Potřebujeme n>jaký druh databáze, která by nám dovolila nalézt požadovaný bankovní účet podle jména vlastníka (nebo spíše podle čísla účtu, protože jedna osoba může mít více účtů a také více lidí může mít stejné jméno).
V kolekci potřebujeme n>co vyhledat podle jednoznačného klíče… hmmm — to vypadá na použití slovníku! (Připomeňme si, že pro takto pojmenovanou strukturu jazyka Python lze použít i pojem vyhledávací tabulka.) Podívejme se, jak bychom použili strukturu typu slovník (dictionary) pro uložení dynamicky vytvořených objektů.
from bankovniucet import * import time # Funkce pro generování unikátních identifikačních čísel. def ziskejDalsiID(): ok = raw_input("Vytvořit účet [a/n]? ") if ok[0] in 'aA': # v případ> souhlasu... id = time.time() # použij aktuální čas jako základ ID, id = int(id) % 10000 # převeď na max. 4místné celé číslo # Poznámka překladatele: time.time() vrací reálné číslo # odpovídající času v sekundách (s přesností obvykle # lepší než 1 sekunda). else: id = -1 # tato hodnota zastaví cyklus return id tabulkaUctu = {} # nový slovník while 1: # nekonečný cyklus id = ziskejDalsiID() if id == -1: break # break násiln> ukončí cyklus while vklad = float(raw_input("Počáteční vklad? ")) # Identifikaci id použijeme pro vytvoření nové položky tabulky. tabulkaUctu[id] = BankovniUcet(vklad) print "Byl vytvořen nový účet číslo %04d s vkladem %0.2f" % (id, vklad) # Zobrazíme si zůstatky na všech účtech. for id in tabulkaUctu: # Poznámka překladatele: od Python 2.0 není nutné # v zápisu cyklu uvád>t tabulkaUctu.keys(), jak tomu bylo # u starších verzí. print "%04d\t%0.2f" % (id, tabulkaUctu[id].zustatek()) # Nyní vyhledáme konkrétní účet. Pokud chcete ukončit # program, vložte nečíselnou hodnotu. while 1: id = int(raw_input("Zadejte číslo účtu: ")) if id in tabulkaUctu: print "Zůstatek = %0.2f" % tabulkaUctu[id].zustatek() else: print "Chybné číslo účtu."
V roli klíče, který se používá pro vyhledávání ve slovníku, může být samozřejm> použito cokoliv, co jednoznačn> identifikuje objekt. Může to být n>jaký z jeho atributů, například jméno. Cokoliv, co je jednoznačné. Možná teď pro vás bude užitečné, když si znovu projdete kapitolu Data, datové typy a prom>nné, a přečtete si konkrétn> část, která se týká slovníků (vyhledávacích tabulek). Jsou to opravdu velmi užitečné kontejnery.
Výše uvedené řešení má jednu nevýhodu. Jakmile ukončíte program, všechny
údaje budou ztraceny. Potřebujeme tedy n>jaký způsob pro ukládání objektů. S
vaším postupujícím programátorským růstem se pozd>ji naučíte, jak pro tento
účel používat databáze. Ale v tomto okamžiku nám bude pro ukládání a
op>tovné načítání objektů stačit textový soubor. Python definuje moduly
(zvané Pickle a Shelve), které umí s objekty v
tomto smyslu zacházet efektivn>ji. Ale ukažme si rad>ji generický (tedy obecn>
použitelný) způsob, který by fungoval v libovolném programovacím
jazyce. Technický termín pro schopnost uložení a op>tovné obnovení stavu
objektů se shodou okolností nazývá persistence. (Tento pojem se chápe jako
termín a obvykle se nepřekládá. Z hlediska významu bychom jej ale mohli
přeložit jako schopnost přetrvat — rozumí se uchovat svůj
stav po dobu, kdy aplikace neb>ží.)
Generický (tedy
obecn> použitelný) způsob spočívá ve vytvoření metod
save a restore v objektu nejvyšší úrovn> (poznámka překladatele: autor
má na mysli bázovou třídu) a předefinujeme je v každé odvozené tříd> tak, že
nejdříve zavolají zd>d>nou verzi a poté přidají své lokáln> definované
atributy. Poznámka překladatele: Tyto metody nebudeme
překládat (save [sejv] = uložit; restore [ristór] = obnovit), protože se jim
typicky dávají práv> tato anglická jména.
class A:
def __init__(self, x, y):
self.x = x
self.y = y
def save(self, fn):
f = open(fn, "w")
# Poznámka překladatele: Od verze Python 2 by se
# m>la dávat přednost zápisu f = file(fn, "w")
f.write(str(self.x) + '\n') # převeď na řet>zec
f.write(str(self.y) + '\n')
return f # do stejného souboru budou své hodnoty
# připisovat objekty odvozených tříd
def restore(self, fn):
f = open(fn)
self.x = int(f.readline()) # převeď zp>t na původní typ
self.y = int(f.readline())
return f
class B(A):
def __init__(self, x, y, z):
A.__init__(self, x, y)
self.z = z
def save(self, fn):
f = A.save(self, fn) # zavolej rodičovskou metodu
f.write(str(self.z) + '\n') # přidej vlastní hodnoty
return f # pro případné další potomky
def restore(self, fn):
f = A.restore(self, fn)
self.z = int(f.readline())
return f
# Vytvoříme instance.
a = A(1, 2)
b = B(3, 4, 5)
# Uložíme instance.
a.save('a.txt').close() # nezapomeň uzavřít soubor
b.save('b.txt').close()
# Obnovíme instance.
newA = A(5, 6)
newA.restore('a.txt').close() # nezapomeň uzavřít soubor
newB = B(7, 8, 9)
newB.restore('b.txt').close()
print "A: ", newA.x, newA.y
print "B: ", newB.x, newB.y, newB.z
Poznámka: Vytisknou se hodnoty, které jsou obnoveny načtením ze souborů, nikoliv hodnoty, které jsme použili při vytváření instancí.
Klíčovým požadavkem je předefinování metod save a
restore v každé tříd> a také to, aby byla nejdříve volána
rodičovská metoda (tj.
metoda bázové třídy). V odvozené tříd> se pak musíme postarat pouze o
přidané atributy. Způsob, jakým atribut převedeme na řet>zec a uložíme,
závisí samozřejm> na nás, ale musí být umíst>n na zvláštním řádku. Při
obnovování jednoduše obrátíme postup, který jsme použili při ukládání.
Poznámka překladatele: V uvedeném příkladu se skrývá problém. Otevřený soubor by se m>l vždy explicitn> uzavřít. V tomto smyslu je uvedené řešení pom>rn> nedokonalé. Pokud se považujete za začátečníky, soustřeďte se především na hlavní myšlenku, kterou autor prezentuje. Nespoléhejte na správnost příkladu v detailech.
Při práci se soubory by se m>lo používat nepsané pravidlo, které říká, že
soubor by se m>l uzavírat na stejné úrovni, kde se otevřel. Tím se obvykle
vyhneme komplikacím s předáváním odpov>dnosti za uzavření souboru do
volaného kódu nebo naopak do volajícího kódu. Z tohoto pravidla vyplývá, že
by se místo textového jména souboru m>l metodám save() a
restore() předávat již objekt otevřeného souboru.
Další problém spočívá v tom, že explicitn> určujeme jméno souboru, do kterého se objekt ukládá. Pokud byste n>kdy takto vybudovanou třídu cht>li použít například v jiné aplikaci, mohli byste se setkat s komplikacemi.
V tomto míst> pokládám za vhodné zmínit se o tom, že u odvozené třídy
musíme sami zajistit uvnitř metody __init__() volání
stejnojmenné metody bázové třídy, které jako první parametr předáváme
self.
Doufám, že vám tato kapitola dala přičichnout k objektov> orientovanému programování. Další informace a příklady můžete nalézt v n>jaké dalším on-line učebnici nebo si můžete přečíst n>kterou z knih, o kterých jsme se zmiňovali na začátku.
Pokud vás napadne, co by se dalo na překladu této kapitoly vylepšit, zašlete e-mail odklepnutím Tím budou do dopisu automaticky vloženy informace o tomto HTML dokumentu.
$Id: cztutclass.html,v 1.6 2004/08/31 11:55:12 prikryl Exp $