Dědičnost

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íš.

Přepisování metod a 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!

Polymorfismus

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 volant”, stejně jako „každé kotě 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.)

Generalizace

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&#x11B;di&#x10D;nost</h1>\n<p>Minule jsme probraly t&#x159;&#xED;dy &#x2013; jako p&#x159;&#xED;klad jsme si\nuk&#xE1;zaly t&#x159;&#xED;du pro ko&#x165;&#xE1;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\">&quot;{}: {} mi chutn&#xE1;!&quot;</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\">&quot;{}: M&#x148;au!&quot;</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&#x11B;lat podobnou t&#x159;&#xED;du pro &#x161;t&#x11B;&#x148;&#xE1;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\">&quot;{}: {} mi chutn&#xE1;!&quot;</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\">&quot;{}: Haf!&quot;</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&#x11B;t&#x161;ina k&#xF3;du je stejn&#xE1;!\nKdybys m&#x11B;la napsat i t&#x159;&#xED;du pro ku&#x159;&#xE1;tka, k&#x16F;zl&#xE1;tka,\nsl&#x16F;&#x148;&#xE1;tka a h&#xE1;&#x10F;&#xE1;tka, bez Ctrl+C by to bylo docela nudn&#xE9;.\nA proto&#x17E;e jsou program&#xE1;to&#x159;i l&#xED;n&#xED; ps&#xE1;t stejn&#xFD; k&#xF3;d\nn&#x11B;kolikr&#xE1;t (a hlavn&#x11B; ho potom udr&#x17E;ovat), vymysleli\nmechanismus, jak se toho vyvarovat. Jak?</p>\n<p>Ko&#x165;&#xE1;tka i &#x161;t&#x11B;&#x148;&#xE1;tka jsou zv&#xED;&#x159;&#xE1;tka.\nM&#x16F;&#x17E;e&#x161; si vytvo&#x159;it t&#x159;&#xED;du spole&#x10D;nou pro v&#x161;echna\nzv&#xED;&#x159;&#xE1;tka a do n&#xED; napsat v&#x161;echno, co je spole&#x10D;n&#xE9;.\nVe t&#x159;&#xED;d&#xE1;ch pro jednotliv&#xE9; druhy zv&#xED;&#x159;at pak zbude jen\nto, co se li&#x161;&#xED;.\nV Pythonu se to d&#x11B;l&#xE1; 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\">&quot;{}: {} mi chutn&#xE1;!&quot;</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\">&quot;{}: M&#x148;au!&quot;</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\">&quot;{}: Haf!&quot;</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\">&apos;Micka&apos;</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\">&apos;Azorek&apos;</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\">&apos;my&#x161;&apos;</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\">&apos;kost&apos;</span><span class=\"p\">)</span>\n</pre></div><p>Jak to funguje?\nP&#x159;&#xED;kazem <code>class Kotatko(Zviratko):</code>\n&#x159;&#xED;k&#xE1;&#x161; Pythonu, &#x17E;e t&#x159;&#xED;da <code>Kotatko</code>\n<em>d&#x11B;d&#xED;</em> ze t&#x159;&#xED;dy <code>Zviratko</code>\n(angl. <em>inherits</em> from <code>Zviratko</code>).\nP&#x159;&#xED;padn&#x11B; se m&#x16F;&#x17E;e&#x161; setkat s&#xA0;jin&#xFD;mi term&#xED;ny:\n&#x201E;je odvozen&#xE1;&#x201D; ze t&#x159;&#xED;dy <code>Zviratko</code>,\n(angl. <em>derived from</em>),\nnebo ji &#x201C;roz&#x161;i&#x159;uje&#x201D; (angl. <em>extends</em>).\nA kdy&#x17E; u&#x17E; jsme u terminologie, odvozen&#xFD;m t&#x159;&#xED;d&#xE1;m se\n&#x159;&#xED;k&#xE1; taky <em>podt&#x159;&#xED;dy</em> (angl. <em>subclasses</em>)\na <code>Zviratko</code> je tu <em>nadt&#x159;&#xED;da</em>\n(angl. <em>superclass</em>).</p>\n<p>Kdy&#x17E; potom Python hled&#xE1; n&#x11B;jakou metodu\n(nebo jin&#xFD; atribut), t&#x159;eba <code>micka.snez</code>,\na nenajde ji p&#x159;&#xED;mo ve t&#x159;&#xED;d&#x11B; dan&#xE9;ho objektu (u n&#xE1;s\n<code>Kotatko</code>), pod&#xED;v&#xE1; se do nadt&#x159;&#xED;dy.\nTak&#x17E;e v&#x161;echno, co je definovan&#xE9; pro\n<code>Zviratko</code>, plat&#xED; i pro ko&#x165;&#xE1;tka.\nPokud to tedy v&#xFD;slovn&#x11B; nezm&#x11B;n&#xED;&#x161;.</p>\n<h2>P&#x159;episov&#xE1;n&#xED; metod a <code>super()</code></h2>\n<p>Kdy&#x17E; se ti nebude l&#xED;bit chov&#xE1;n&#xED; n&#x11B;kter&#xE9; metody\nv nadt&#x159;&#xED;d&#x11B;, sta&#x10D;&#xED; d&#xE1;t metodu stejn&#xE9;ho jm&#xE9;na do\npodt&#x159;&#xED;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\">&quot;{}: {} mi v&#x16F;bec nechutn&#xE1;!&quot;</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\">&apos;Micka&apos;</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\">&apos;granule&apos;</span><span class=\"p\">)</span>\n</pre></div><div class=\"admonition python\"><p>Je to podobn&#xE9; jako kdy&#x17E; jsme minule p&#x159;episovaly\natribut pomoc&#xED; <code>micka.zamnoukej = 12345</code>.\nPython atributy hled&#xE1; nap&#x159;ed na samotn&#xE9;m objektu,\npotom na t&#x159;&#xED;d&#x11B; toho objektu a pak na nadt&#x159;&#xED;d&#x11B;\n(a p&#x159;&#xED;padn&#x11B; dal&#x161;&#xED;ch nadt&#x159;&#xED;d&#xE1;ch t&#xE9; nadt&#x159;&#xED;dy).</p>\n</div><p>Ob&#x10D;as se m&#x16F;&#x17E;e st&#xE1;t, &#x17E;e v takov&#xE9;to p&#x159;epsan&#xE9; metod&#x11B; bude&#x161;\npot&#x159;ebovat pou&#x17E;&#xED;t p&#x16F;vodn&#xED; funk&#x10D;nost, jen bude&#x161; cht&#xED;t ud&#x11B;lat je&#x161;t&#x11B; n&#x11B;co nav&#xED;c.\nTo um&#xED; za&#x159;&#xED;dit speci&#xE1;ln&#xED; funkce <code>super()</code>,\nkter&#xE1; umo&#x17E;&#x148;uje volat metody z nadt&#x159;&#xED;dy.\nT&#x159;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\">&quot;({} na {} chv&#xED;li fascinovan&#x11B; kouk&#xE1;)&quot;</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\">&apos;Micka&apos;</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\">&apos;granule&apos;</span><span class=\"p\">)</span>\n</pre></div><p>Pozor na to, &#x17E;e takhle volan&#xE9; metod&#x11B; mus&#xED;&#x161; d&#xE1;t v&#x161;echny\nargumenty, kter&#xE9; pot&#x159;ebuje (krom&#x11B; <code>self</code>,\nkter&#xFD; se jako obvykle dopln&#xED; automaticky).\nToho se d&#xE1; i vyu&#x17E;&#xED;t &#x2013; m&#x16F;&#x17E;e&#x161; pou&#x17E;&#xED;t i jin&#xE9; argumenty\nne&#x17E; dostala p&#x16F;vodn&#xED; 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\">&apos;s&apos;</span><span class=\"p\">,</span> <span class=\"s1\">&apos;sss&apos;</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\">&apos;S&apos;</span><span class=\"p\">,</span> <span class=\"s1\">&apos;Sss&apos;</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\">&apos;Stanislav&apos;</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\">&apos;my&#x161;&apos;</span><span class=\"p\">)</span>\n</pre></div><p>Jak je vid&#x11B;t, <code>super()</code> se d&#xE1; bez probl&#xE9;m&#x16F;\nkombinovat se speci&#xE1;ln&#xED;mi metodami jako <code>__init__</code>.\nDokonce se to d&#x11B;l&#xE1; pom&#x11B;rn&#x11B; &#x10D;asto!</p>\n<h2>Polymorfismus</h2>\n<p>Program&#xE1;to&#x159;i nezavedli d&#x11B;di&#x10D;nost jen proto, &#x17E;e jsou\nl&#xED;n&#xED; a necht&#x11B;j&#xED; ps&#xE1;t dvakr&#xE1;t stejn&#xFD; k&#xF3;d.\nTo je sice dobr&#xFD; d&#x16F;vod, ale nadt&#x159;&#xED;dy maj&#xED; je&#x161;t&#x11B; jednu\nd&#x16F;le&#x17E;itou vlastnost: kdy&#x17E; v&#xED;me, &#x17E;e ka&#x17E;d&#xE9;\n<code>Kotatko</code> nebo <code>Stenatko</code>\nnebo jak&#xE1;koli jin&#xE1; podt&#x159;&#xED;da je zv&#xED;&#x159;&#xE1;tko,\nm&#x16F;&#x17E;eme si ud&#x11B;lat seznam zv&#xED;&#x159;&#xE1;tek s t&#xED;m,\n&#x17E;e n&#xE1;m pak bude jedno, jak&#xE1; p&#x159;esn&#x11B; zv&#xED;&#x159;&#xE1;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\">&quot;{}: {} mi chutn&#xE1;!&quot;</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\">&quot;{}: M&#x148;au!&quot;</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\">&quot;{}: Haf!&quot;</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\">&apos;Micka&apos;</span><span class=\"p\">),</span> <span class=\"n\">Stenatko</span><span class=\"p\">(</span><span class=\"s1\">&apos;Azorek&apos;</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\">&apos;fl&#xE1;kota&apos;</span><span class=\"p\">)</span>\n</pre></div><p>Tohle je docela d&#x16F;le&#x17E;it&#xE1; vlastnost podt&#x159;&#xED;d:\nkdy&#x17E; m&#xE1;&#x161; n&#x11B;jak&#xE9; <code>Kotatko</code>, m&#x16F;&#x17E;e&#x161; ho pou&#x17E;&#xED;t\nkdekoliv kde program o&#x10D;ek&#xE1;v&#xE1; <code>Zviratko</code>,\nproto&#x17E;e ka&#x17E;d&#xE9; ko&#x165;&#xE1;tko <em>je</em> zv&#xED;&#x159;&#xE1;tko.</p>\n<div class=\"admonition note\"><p>Tohle je docela dobr&#xE1; pom&#x16F;cka pro p&#x159;&#xED;pady, kdy nebude&#x161; v&#x11B;d&#x11B;t\nkterou t&#x159;&#xED;du pod&#x11B;dit z kter&#xE9;.\nKa&#x17E;d&#xE9; <em>ko&#x165;&#xE1;tko</em> nebo <em>&#x161;t&#x11B;&#x148;&#xE1;tko</em>\nje <em>zv&#xED;&#x159;&#xE1;tko</em>, ka&#x17E;d&#xE1; <em>chata</em>\nnebo <em>panel&#xE1;k</em> je <em>staven&#xED;</em>.\nV takov&#xFD;ch p&#x159;&#xED;padech d&#xE1;v&#xE1; d&#x11B;di&#x10D;nost smysl.</p>\n<p>N&#x11B;kdy se ale stane, &#x17E;e tuhle pom&#x16F;cku zkus&#xED;&#x161; pou&#x17E;&#xED;t a vyjde ti\nnesmysl jako &#x201E;ka&#x17E;d&#xE9; auto je volant&#x201D;.\nV takov&#xE9;m p&#x159;&#xED;pad&#x11B; d&#x11B;di&#x10D;nost nepou&#x17E;&#xED;vej.\nI kdy&#x17E; jak auto tak volant se daj&#xED; &#x201E;oto&#x10D;it doprava&#x201D;,\nu ka&#x17E;d&#xE9;ho to znamen&#xE1; n&#x11B;co jin&#xE9;ho &#x2013; a ur&#x10D;it&#x11B; nejde auto\npou&#x17E;&#xED;t kdekoli, kde bych cht&#x11B;la pou&#x17E;&#xED;t volant.\nTak&#x17E;e v tomto p&#x159;&#xED;pad&#x11B; je lep&#x161;&#xED; si &#x159;&#xED;ct &#x201E;ka&#x17E;d&#xE9; auto\n<em>m&#xE1;</em> volant&#x201D;, stejn&#x11B; jako &#x201E;ka&#x17E;d&#xE9; kot&#x11B;\n<em>m&#xE1;</em> jm&#xE9;no&#x201D;, ud&#x11B;lat dv&#x11B; nez&#xE1;visl&#xE9; t&#x159;&#xED;dy a napsat n&#x11B;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&#x17E; bude n&#x11B;kdy n&#x11B;jak&#xFD; vystudovan&#xFD; informatik nespokojen&#xFD;\ns t&#xED;m, &#x17E;e poru&#x161;uje&#x161;\n<a href=\"https://en.wikipedia.org/wiki/Liskov_substitution_principle\">Liskovov&#xE9; substitu&#x10D;n&#xED; princip</a>,\njde o pr&#xE1;v&#x11B; tento probl&#xE9;m.)</p>\n</div><h2>Generalizace</h2>\n<p>Kdy&#x17E; se te&#x10F; pod&#xED;v&#xE1;&#x161; na funkce <code>zamnoukej</code>\na <code>zastekej</code>, mo&#x17E;n&#xE1; t&#x11B; napadne, &#x17E;e by se\ndaly pojmenovat l&#xE9;pe, aby se daly pou&#x17E;&#xED;t pro v&#x161;echna\nzv&#xED;&#x159;ata, podobn&#x11B; jako <code>snez</code>.\nBude nejlep&#x161;&#xED; je p&#x159;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\">&quot;{}: {} mi chutn&#xE1;!&quot;</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\">&quot;{}: M&#x148;au!&quot;</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\">&quot;{}: Haf!&quot;</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\">&apos;Micka&apos;</span><span class=\"p\">),</span> <span class=\"n\">Stenatko</span><span class=\"p\">(</span><span class=\"s1\">&apos;Azorek&apos;</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\">&apos;fl&#xE1;kota&apos;</span><span class=\"p\">)</span>\n</pre></div><p>Jak tenhle p&#x159;&#xED;klad nazna&#x10D;uje, ps&#xE1;t nadt&#x159;&#xED;dy, ze kter&#xFD;ch se dob&#x159;e d&#x11B;d&#xED;,\nnen&#xED; jednoduch&#xE9;. Zvl&#xE1;&#x161;&#x165; to plat&#xED;, kdyby se z&#xA0;nich m&#x11B;lo d&#x11B;dit v&#xA0;jin&#xE9;m\nprogramu, ne&#x17E; kde je nadt&#x159;&#xED;da.\nI z&#xA0;toho d&#x16F;vodu je dobr&#xE9; d&#x11B;di&#x10D;nost pou&#x17E;&#xED;vat hlavn&#x11B; v r&#xE1;mci sv&#xE9;ho k&#xF3;du:\nnedoporu&#x10D;uji d&#x11B;dit od t&#x159;&#xED;d, kter&#xE9; napsali ostatn&#xED; (jako <code>bool</code> nebo\n<code>pyglet.sprite.Sprite</code>), pokud autor nadt&#x159;&#xED;dy v&#xFD;slovn&#x11B; nezm&#xED;n&#xED;, &#x17E;e (a jak) se\nz&#xA0;n&#xED; d&#x11B;dit m&#xE1;.</p>\n<p>A to je zat&#xED;m o t&#x159;&#xED;d&#xE1;ch v&#x161;e. U&#x17E; toho v&#xED;&#x161; dost na to,\naby sis napsala vlastn&#xED; zoo :)</p>\n<p>Nebo <a href=\"/2018/pyladies-praha-jaro-cznic/projects/asteroids/\">hru</a>?</p>\n\n\n        "
    }
  }
}