Minule jsme probraly třídy – jako příklad jsme si ukázaly třídu pro koťátka:
class Kotatko:
def __init__(self, jmeno):
self.jmeno = jmeno
def snez(self, jidlo):
print("{}: {} mi chutná!".format(self.jmeno, jidlo))
def zamnoukej(self):
print("{}: Mňau!".format(self.jmeno))
Zkus si udělat podobnou třídu pro štěňátka:
class Stenatko:
def __init__(self, jmeno):
self.jmeno = jmeno
def snez(self, jidlo):
print("{}: {} mi chutná!".format(self.jmeno, jidlo))
def zastekej(self):
print("{}: Haf!".format(self.jmeno))
Většina kódu je stejná! Kdybys měla napsat i třídu pro kuřátka, kůzlátka, slůňátka a háďátka, bez Ctrl+C by to bylo docela nudné. A protože jsou programátoři líní psát stejný kód několikrát (a hlavně ho potom udržovat), vymysleli mechanismus, jak se toho vyvarovat. Jak?
Koťátka i štěňátka jsou zvířátka. Můžeš si vytvořit třídu společnou pro všechna zvířátka a do ní napsat všechno, co je společné. Ve třídách pro jednotlivé druhy zvířat pak zbude jen to, co se liší. V Pythonu se to dělá takto:
class Zviratko:
def __init__(self, jmeno):
self.jmeno = jmeno
def snez(self, jidlo):
print("{}: {} mi chutná!".format(self.jmeno, jidlo))
class Kotatko(Zviratko):
def zamnoukej(self):
print("{}: Mňau!".format(self.jmeno))
class Stenatko(Zviratko):
def zastekej(self):
print("{}: Haf!".format(self.jmeno))
micka = Kotatko('Micka')
azorek = Stenatko('Azorek')
micka.zamnoukej()
azorek.zastekej()
micka.snez('myš')
azorek.snez('kost')
Jak to funguje?
Příkazem class Kotatko(Zviratko):
říkáš Pythonu, že třída Kotatko
dědí ze třídy Zviratko
(angl. inherits from Zviratko
).
Případně se můžeš setkat s jinými termíny:
„je odvozená” ze třídy Zviratko
,
(angl. derived from),
nebo ji “rozšiřuje” (angl. extends).
A když už jsme u terminologie, odvozeným třídám se
říká taky podtřídy (angl. subclasses)
a Zviratko
je tu nadtřída
(angl. superclass).
Když potom Python hledá nějakou metodu
(nebo jiný atribut), třeba micka.snez
,
a nenajde ji přímo ve třídě daného objektu (u nás
Kotatko
), podívá se do nadtřídy.
Takže všechno, co je definované pro
Zviratko
, platí i pro koťátka.
Pokud to tedy výslovně nezměníš.
super()
Když se ti nebude líbit chování některé metody v nadtřídě, stačí dát metodu stejného jména do podtřídy:
class Kotatko(Zviratko):
def snez(self, jidlo):
print("{}: {} mi vůbec nechutná!".format(self.jmeno, jidlo))
micka = Kotatko('Micka')
micka.snez('granule')
Je to podobné jako když jsme minule přepisovaly
atribut pomocí micka.zamnoukej = 12345
.
Python atributy hledá napřed na samotném objektu,
potom na třídě toho objektu a pak na nadtřídě
(a případně dalších nadtřídách té nadtřídy).
Občas se může stát, že v takovéto přepsané metodě budeš
potřebovat použít původní funkčnost, jen budeš chtít udělat ještě něco navíc.
To umí zařídit speciální funkce super()
,
která umožňuje volat metody z nadtřídy.
Třeba takhle:
class Kotatko(Zviratko):
def snez(self, jidlo):
print("({} na {} chvíli fascinovaně kouká)".format(self.jmeno, jidlo))
super().snez(jidlo)
micka = Kotatko('Micka')
micka.snez('granule')
Pozor na to, že takhle volané metodě musíš dát všechny
argumenty, které potřebuje (kromě self
,
který se jako obvykle doplní automaticky).
Toho se dá i využít – můžeš použít i jiné argumenty
než dostala původní funkce:
class Hadatko(Zviratko):
def __init__(self, jmeno):
jmeno = jmeno.replace('s', 'sss')
jmeno = jmeno.replace('S', 'Sss')
super().__init__(jmeno)
standa = Hadatko('Stanislav')
standa.snez('myš')
Jak je vidět, super()
se dá bez problémů
kombinovat se speciálními metodami jako __init__
.
Dokonce se to dělá poměrně často!
Programátoři nezavedli dědičnost jen proto, že jsou
líní a nechtějí psát dvakrát stejný kód.
To je sice dobrý důvod, ale nadtřídy mají ještě jednu
důležitou vlastnost: když víme, že každé
Kotatko
nebo Stenatko
nebo jakákoli jiná podtřída je zvířátko,
můžeme si udělat seznam zvířátek s tím,
že nám pak bude jedno, jaká přesně zvířátka to jsou:
class Zviratko:
def __init__(self, jmeno):
self.jmeno = jmeno
def snez(self, jidlo):
print("{}: {} mi chutná!".format(self.jmeno, jidlo))
class Kotatko(Zviratko):
def zamnoukej(self):
print("{}: Mňau!".format(self.jmeno))
class Stenatko(Zviratko):
def zastekej(self):
print("{}: Haf!".format(self.jmeno))
zviratka = [Kotatko('Micka'), Stenatko('Azorek')]
for zviratko in zviratka:
zviratko.snez('flákota')
Tohle je docela důležitá vlastnost podtříd:
když máš nějaké Kotatko
, můžeš ho použít
kdekoliv kde program očekává Zviratko
,
protože každé koťátko je zvířátko.
Tohle je docela dobrá pomůcka pro případy, kdy nebudeš vědět kterou třídu podědit z které. Každé koťátko nebo štěňátko je zvířátko, každá chata nebo panelák je stavení. V takových případech dává dědičnost smysl.
Někdy se ale stane, že tuhle pomůcku zkusíš použít a vyjde ti nesmysl jako „každé auto je volant”. V takovém případě dědičnost nepoužívej. I když jak auto tak volant se dají „otočit doprava”, u každého to znamená něco jiného – a určitě nejde auto použít kdekoli, kde bych chtěla použít volant. Takže v tomto případě je lepší si říct „každé auto má volant”, stejně jako „každé kotě má jméno”, udělat dvě nezávislé třídy a napsat něco jako:
class Auto:
def __init__(self):
self.volant = Volant()
(A až bude někdy nějaký vystudovaný informatik nespokojený s tím, že porušuješ Liskovové substituční princip, jde o právě tento problém.)
Když se teď podíváš na funkce zamnoukej
a zastekej
, možná tě napadne, že by se
daly pojmenovat lépe, aby se daly použít pro všechna
zvířata, podobně jako snez
.
Bude nejlepší je přejmenovat:
class Zviratko:
def __init__(self, jmeno):
self.jmeno = jmeno
def snez(self, jidlo):
print("{}: {} mi chutná!".format(self.jmeno, jidlo))
class Kotatko(Zviratko):
def udelej_zvuk(self):
print("{}: Mňau!".format(self.jmeno))
class Stenatko(Zviratko):
def udelej_zvuk(self):
print("{}: Haf!".format(self.jmeno))
zviratka = [Kotatko('Micka'), Stenatko('Azorek')]
for zviratko in zviratka:
zviratko.udelej_zvuk()
zviratko.snez('flákota')
Jak tenhle příklad naznačuje, psát nadtřídy, ze kterých se dobře dědí,
není jednoduché. Zvlášť to platí, kdyby se z nich mělo dědit v jiném
programu, než kde je nadtřída.
I z toho důvodu je dobré dědičnost používat hlavně v rámci svého kódu:
nedoporučuji dědit od tříd, které napsali ostatní (jako bool
nebo
pyglet.sprite.Sprite
), pokud autor nadtřídy výslovně nezmíní, že (a jak) se
z ní dědit má.
A to je zatím o třídách vše. Už toho víš dost na to, aby sis napsala vlastní zoo :)
Nebo hru?
{ "data": { "sessionMaterial": { "id": "session-material:2018/pyladies-praha-jaro-cznic:class:1", "title": "Dědičnost", "html": "\n \n \n\n <h1>Dědičnost</h1>\n<p>Minule jsme probraly třídy – jako příklad jsme si\nukázaly třídu pro koťátka:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Kotatko</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jmeno</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span> <span class=\"o\">=</span> <span class=\"n\">jmeno</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">snez</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: {} mi chutná!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">))</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">zamnoukej</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: Mňau!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">))</span>\n</pre></div><p>Zkus si udělat podobnou třídu pro štěňátka:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Stenatko</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jmeno</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span> <span class=\"o\">=</span> <span class=\"n\">jmeno</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">snez</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: {} mi chutná!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">))</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">zastekej</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: Haf!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">))</span>\n</pre></div><p>Většina kódu je stejná!\nKdybys měla napsat i třídu pro kuřátka, kůzlátka,\nslůňátka a háďátka, bez Ctrl+C by to bylo docela nudné.\nA protože jsou programátoři líní psát stejný kód\nněkolikrát (a hlavně ho potom udržovat), vymysleli\nmechanismus, jak se toho vyvarovat. Jak?</p>\n<p>Koťátka i štěňátka jsou zvířátka.\nMůžeš si vytvořit třídu společnou pro všechna\nzvířátka a do ní napsat všechno, co je společné.\nVe třídách pro jednotlivé druhy zvířat pak zbude jen\nto, co se liší.\nV Pythonu se to dělá takto:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Zviratko</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jmeno</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span> <span class=\"o\">=</span> <span class=\"n\">jmeno</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">snez</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: {} mi chutná!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">))</span>\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Kotatko</span><span class=\"p\">(</span><span class=\"n\">Zviratko</span><span class=\"p\">):</span>\n <span class=\"k\">def</span> <span class=\"nf\">zamnoukej</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: Mňau!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">))</span>\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Stenatko</span><span class=\"p\">(</span><span class=\"n\">Zviratko</span><span class=\"p\">):</span>\n <span class=\"k\">def</span> <span class=\"nf\">zastekej</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: Haf!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">))</span>\n\n\n<span class=\"n\">micka</span> <span class=\"o\">=</span> <span class=\"n\">Kotatko</span><span class=\"p\">(</span><span class=\"s1\">'Micka'</span><span class=\"p\">)</span>\n<span class=\"n\">azorek</span> <span class=\"o\">=</span> <span class=\"n\">Stenatko</span><span class=\"p\">(</span><span class=\"s1\">'Azorek'</span><span class=\"p\">)</span>\n<span class=\"n\">micka</span><span class=\"o\">.</span><span class=\"n\">zamnoukej</span><span class=\"p\">()</span>\n<span class=\"n\">azorek</span><span class=\"o\">.</span><span class=\"n\">zastekej</span><span class=\"p\">()</span>\n<span class=\"n\">micka</span><span class=\"o\">.</span><span class=\"n\">snez</span><span class=\"p\">(</span><span class=\"s1\">'myš'</span><span class=\"p\">)</span>\n<span class=\"n\">azorek</span><span class=\"o\">.</span><span class=\"n\">snez</span><span class=\"p\">(</span><span class=\"s1\">'kost'</span><span class=\"p\">)</span>\n</pre></div><p>Jak to funguje?\nPříkazem <code>class Kotatko(Zviratko):</code>\nříkáš Pythonu, že třída <code>Kotatko</code>\n<em>dědí</em> ze třídy <code>Zviratko</code>\n(angl. <em>inherits</em> from <code>Zviratko</code>).\nPřípadně se můžeš setkat s jinými termíny:\n„je odvozená” ze třídy <code>Zviratko</code>,\n(angl. <em>derived from</em>),\nnebo ji “rozšiřuje” (angl. <em>extends</em>).\nA když už jsme u terminologie, odvozeným třídám se\nříká taky <em>podtřídy</em> (angl. <em>subclasses</em>)\na <code>Zviratko</code> je tu <em>nadtřída</em>\n(angl. <em>superclass</em>).</p>\n<p>Když potom Python hledá nějakou metodu\n(nebo jiný atribut), třeba <code>micka.snez</code>,\na nenajde ji přímo ve třídě daného objektu (u nás\n<code>Kotatko</code>), podívá se do nadtřídy.\nTakže všechno, co je definované pro\n<code>Zviratko</code>, platí i pro koťátka.\nPokud to tedy výslovně nezměníš.</p>\n<h2>Přepisování metod a <code>super()</code></h2>\n<p>Když se ti nebude líbit chování některé metody\nv nadtřídě, stačí dát metodu stejného jména do\npodtřídy:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Kotatko</span><span class=\"p\">(</span><span class=\"n\">Zviratko</span><span class=\"p\">):</span>\n <span class=\"k\">def</span> <span class=\"nf\">snez</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: {} mi vůbec nechutná!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">))</span>\n\n\n<span class=\"n\">micka</span> <span class=\"o\">=</span> <span class=\"n\">Kotatko</span><span class=\"p\">(</span><span class=\"s1\">'Micka'</span><span class=\"p\">)</span>\n<span class=\"n\">micka</span><span class=\"o\">.</span><span class=\"n\">snez</span><span class=\"p\">(</span><span class=\"s1\">'granule'</span><span class=\"p\">)</span>\n</pre></div><div class=\"admonition python\"><p>Je to podobné jako když jsme minule přepisovaly\natribut pomocí <code>micka.zamnoukej = 12345</code>.\nPython atributy hledá napřed na samotném objektu,\npotom na třídě toho objektu a pak na nadtřídě\n(a případně dalších nadtřídách té nadtřídy).</p>\n</div><p>Občas se může stát, že v takovéto přepsané metodě budeš\npotřebovat použít původní funkčnost, jen budeš chtít udělat ještě něco navíc.\nTo umí zařídit speciální funkce <code>super()</code>,\nkterá umožňuje volat metody z nadtřídy.\nTřeba takhle:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Kotatko</span><span class=\"p\">(</span><span class=\"n\">Zviratko</span><span class=\"p\">):</span>\n <span class=\"k\">def</span> <span class=\"nf\">snez</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"({} na {} chvíli fascinovaně kouká)"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">))</span>\n <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">snez</span><span class=\"p\">(</span><span class=\"n\">jidlo</span><span class=\"p\">)</span>\n\n\n<span class=\"n\">micka</span> <span class=\"o\">=</span> <span class=\"n\">Kotatko</span><span class=\"p\">(</span><span class=\"s1\">'Micka'</span><span class=\"p\">)</span>\n<span class=\"n\">micka</span><span class=\"o\">.</span><span class=\"n\">snez</span><span class=\"p\">(</span><span class=\"s1\">'granule'</span><span class=\"p\">)</span>\n</pre></div><p>Pozor na to, že takhle volané metodě musíš dát všechny\nargumenty, které potřebuje (kromě <code>self</code>,\nkterý se jako obvykle doplní automaticky).\nToho se dá i využít – můžeš použít i jiné argumenty\nnež dostala původní funkce:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Hadatko</span><span class=\"p\">(</span><span class=\"n\">Zviratko</span><span class=\"p\">):</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jmeno</span><span class=\"p\">):</span>\n <span class=\"n\">jmeno</span> <span class=\"o\">=</span> <span class=\"n\">jmeno</span><span class=\"o\">.</span><span class=\"n\">replace</span><span class=\"p\">(</span><span class=\"s1\">'s'</span><span class=\"p\">,</span> <span class=\"s1\">'sss'</span><span class=\"p\">)</span>\n <span class=\"n\">jmeno</span> <span class=\"o\">=</span> <span class=\"n\">jmeno</span><span class=\"o\">.</span><span class=\"n\">replace</span><span class=\"p\">(</span><span class=\"s1\">'S'</span><span class=\"p\">,</span> <span class=\"s1\">'Sss'</span><span class=\"p\">)</span>\n <span class=\"nb\">super</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"n\">jmeno</span><span class=\"p\">)</span>\n\n\n<span class=\"n\">standa</span> <span class=\"o\">=</span> <span class=\"n\">Hadatko</span><span class=\"p\">(</span><span class=\"s1\">'Stanislav'</span><span class=\"p\">)</span>\n<span class=\"n\">standa</span><span class=\"o\">.</span><span class=\"n\">snez</span><span class=\"p\">(</span><span class=\"s1\">'myš'</span><span class=\"p\">)</span>\n</pre></div><p>Jak je vidět, <code>super()</code> se dá bez problémů\nkombinovat se speciálními metodami jako <code>__init__</code>.\nDokonce se to dělá poměrně často!</p>\n<h2>Polymorfismus</h2>\n<p>Programátoři nezavedli dědičnost jen proto, že jsou\nlíní a nechtějí psát dvakrát stejný kód.\nTo je sice dobrý důvod, ale nadtřídy mají ještě jednu\ndůležitou vlastnost: když víme, že každé\n<code>Kotatko</code> nebo <code>Stenatko</code>\nnebo jakákoli jiná podtřída je zvířátko,\nmůžeme si udělat seznam zvířátek s tím,\nže nám pak bude jedno, jaká přesně zvířátka to jsou:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Zviratko</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jmeno</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span> <span class=\"o\">=</span> <span class=\"n\">jmeno</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">snez</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: {} mi chutná!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">))</span>\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Kotatko</span><span class=\"p\">(</span><span class=\"n\">Zviratko</span><span class=\"p\">):</span>\n <span class=\"k\">def</span> <span class=\"nf\">zamnoukej</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: Mňau!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">))</span>\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Stenatko</span><span class=\"p\">(</span><span class=\"n\">Zviratko</span><span class=\"p\">):</span>\n <span class=\"k\">def</span> <span class=\"nf\">zastekej</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: Haf!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">))</span>\n\n<span class=\"n\">zviratka</span> <span class=\"o\">=</span> <span class=\"p\">[</span><span class=\"n\">Kotatko</span><span class=\"p\">(</span><span class=\"s1\">'Micka'</span><span class=\"p\">),</span> <span class=\"n\">Stenatko</span><span class=\"p\">(</span><span class=\"s1\">'Azorek'</span><span class=\"p\">)]</span>\n\n<span class=\"k\">for</span> <span class=\"n\">zviratko</span> <span class=\"ow\">in</span> <span class=\"n\">zviratka</span><span class=\"p\">:</span>\n <span class=\"n\">zviratko</span><span class=\"o\">.</span><span class=\"n\">snez</span><span class=\"p\">(</span><span class=\"s1\">'flákota'</span><span class=\"p\">)</span>\n</pre></div><p>Tohle je docela důležitá vlastnost podtříd:\nkdyž máš nějaké <code>Kotatko</code>, můžeš ho použít\nkdekoliv kde program očekává <code>Zviratko</code>,\nprotože každé koťátko <em>je</em> zvířátko.</p>\n<div class=\"admonition note\"><p>Tohle je docela dobrá pomůcka pro případy, kdy nebudeš vědět\nkterou třídu podědit z které.\nKaždé <em>koťátko</em> nebo <em>štěňátko</em>\nje <em>zvířátko</em>, každá <em>chata</em>\nnebo <em>panelák</em> je <em>stavení</em>.\nV takových případech dává dědičnost smysl.</p>\n<p>Někdy se ale stane, že tuhle pomůcku zkusíš použít a vyjde ti\nnesmysl jako „každé auto je volant”.\nV takovém případě dědičnost nepoužívej.\nI když jak auto tak volant se dají „otočit doprava”,\nu každého to znamená něco jiného – a určitě nejde auto\npoužít kdekoli, kde bych chtěla použít volant.\nTakže v tomto případě je lepší si říct „každé auto\n<em>má</em> volant”, stejně jako „každé kotě\n<em>má</em> jméno”, udělat dvě nezávislé třídy a napsat něco jako:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Auto</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">volant</span> <span class=\"o\">=</span> <span class=\"n\">Volant</span><span class=\"p\">()</span>\n</pre></div><p>(A až bude někdy nějaký vystudovaný informatik nespokojený\ns tím, že porušuješ\n<a href=\"https://en.wikipedia.org/wiki/Liskov_substitution_principle\">Liskovové substituční princip</a>,\njde o právě tento problém.)</p>\n</div><h2>Generalizace</h2>\n<p>Když se teď podíváš na funkce <code>zamnoukej</code>\na <code>zastekej</code>, možná tě napadne, že by se\ndaly pojmenovat lépe, aby se daly použít pro všechna\nzvířata, podobně jako <code>snez</code>.\nBude nejlepší je přejmenovat:</p>\n<div class=\"highlight\"><pre><span></span><span class=\"k\">class</span> <span class=\"nc\">Zviratko</span><span class=\"p\">:</span>\n <span class=\"k\">def</span> <span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jmeno</span><span class=\"p\">):</span>\n <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span> <span class=\"o\">=</span> <span class=\"n\">jmeno</span>\n\n <span class=\"k\">def</span> <span class=\"nf\">snez</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: {} mi chutná!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">,</span> <span class=\"n\">jidlo</span><span class=\"p\">))</span>\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Kotatko</span><span class=\"p\">(</span><span class=\"n\">Zviratko</span><span class=\"p\">):</span>\n <span class=\"k\">def</span> <span class=\"nf\">udelej_zvuk</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: Mňau!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">))</span>\n\n\n<span class=\"k\">class</span> <span class=\"nc\">Stenatko</span><span class=\"p\">(</span><span class=\"n\">Zviratko</span><span class=\"p\">):</span>\n <span class=\"k\">def</span> <span class=\"nf\">udelej_zvuk</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n <span class=\"k\">print</span><span class=\"p\">(</span><span class=\"s2\">"{}: Haf!"</span><span class=\"o\">.</span><span class=\"n\">format</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">jmeno</span><span class=\"p\">))</span>\n\n\n<span class=\"n\">zviratka</span> <span class=\"o\">=</span> <span class=\"p\">[</span><span class=\"n\">Kotatko</span><span class=\"p\">(</span><span class=\"s1\">'Micka'</span><span class=\"p\">),</span> <span class=\"n\">Stenatko</span><span class=\"p\">(</span><span class=\"s1\">'Azorek'</span><span class=\"p\">)]</span>\n\n<span class=\"k\">for</span> <span class=\"n\">zviratko</span> <span class=\"ow\">in</span> <span class=\"n\">zviratka</span><span class=\"p\">:</span>\n <span class=\"n\">zviratko</span><span class=\"o\">.</span><span class=\"n\">udelej_zvuk</span><span class=\"p\">()</span>\n <span class=\"n\">zviratko</span><span class=\"o\">.</span><span class=\"n\">snez</span><span class=\"p\">(</span><span class=\"s1\">'flákota'</span><span class=\"p\">)</span>\n</pre></div><p>Jak tenhle příklad naznačuje, psát nadtřídy, ze kterých se dobře dědí,\nnení jednoduché. Zvlášť to platí, kdyby se z nich mělo dědit v jiném\nprogramu, než kde je nadtřída.\nI z toho důvodu je dobré dědičnost používat hlavně v rámci svého kódu:\nnedoporučuji dědit od tříd, které napsali ostatní (jako <code>bool</code> nebo\n<code>pyglet.sprite.Sprite</code>), pokud autor nadtřídy výslovně nezmíní, že (a jak) se\nz ní dědit má.</p>\n<p>A to je zatím o třídách vše. Už toho víš dost na to,\naby sis napsala vlastní zoo :)</p>\n<p>Nebo <a href=\"/2018/pyladies-praha-jaro-cznic/projects/asteroids/\">hru</a>?</p>\n\n\n " } } }