<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/screen.xsl" media="screen"?>
<lecture>

<meta>
  <maintitle>Python</maintitle>
  <author>Jiří Znamenáček</author>
  <title>Python a objekty</title>
  <date>2026-05-14</date>
  <link><!--a href="http://vyuka.ookami.cz" rel="external">http://vyuka.ookami.cz</a--></link>
  <!--
  <style>
    /* CSSka */
  </style>
  <use-math/>
  <use-x3dom/>
  -->
</meta>
<!--
  „“–…
  ←→ ↑↓ ↔↕
  ↵ aneb &#x21B5; aneb \r aneb CR aneb CarriageReturn
-->


<slide title="Úvod">

    <p>
        Ačkoli všechno v Python'u je objektem (dokonce i <a href="/materialy/python/functions/advanced-2.xml?slajd=7">funkce se v něčem chovají jako objekty</a>!), nepatří Python mezi „tvrdé“ <em>OOP-jazyky</em>, tedy programovací jazyky, které nejen umožňují používat při práci možnosti tzv. <em>objektového programování</em> (dnes už většina), ale dokonce to i vyžadují (jako např. Java).
    </p>
    <p>
        Historicky třeba ani knihovna Python'u nebyla moc objektová, protože úžasné <a href="/materialy/python/modules/_modules.xml">zapouzdření kódu do modulů</a> ho pro většinu použití bohatě nahradí. Nicméně jsou místa, kde je v Python'u použití objektů (resp. tzv. <em>tříd</em>) zcela na místě a nikdo se mu nevyhýbá. A na jednom takovém si základy OOP v následujících slajdech vysvětlíme.
    </p>

</slide>
<slide title="Motivační příklad 1">

    <p>
        Mějme za úkol přebírat odněkud data a kopírovat je do (pythoního) seznamu, ale s vynecháním duplikátů. Kód by mohl vypadat například takto:
    </p>
    <example lang="python">
        data = []
        for prvek in zdroj:
            if prvek not in data:
                data.append(prvek)
    </example>
    <note>
        Ponechme teď stranou, co je zdrojem, jak prvky vypadají a především pak, že dotazovat se pro každý prvek, zda je v seznamu, je jedna z nejpomalejších věcí, které můžeme udělat.
    </note>
    <p>
        Co kdybychom však měli k dispozici datovou strukturu, která by se chovala úplně stejně jako seznam, pouze by nepřipouštěla zanesení duplikátů? Pak by kód mohl vypadat takto:
    </p>
    <example lang="python">
        data = SeznamBezDuplikátů()
        for prvek in zdroj:
            data.append(prvek)
    </example>
    <note>
        S nápadem přišel Vít Novotný.
    </note>
    <p>
        Python umožňuje takovou datovou strukturu vyrobit velmi snadno. Pojďme na to.
    </p>

</slide>
<slide title="Dědění vlastností rodiče">

    <p>
        Chceme připravit datovou strukturu <em>MůjSeznam</em>, která se bude (skoro úplně) chovat jako seznam. To se v Python'u dělá zavedením tzv. <strong>třídy</strong> pomocí klíčového slova <code>class</code>, kde do závorek za jméno vytvářené třídy napíšeme rodiče, jehož vlastnosti chceme podědit:
    </p>
    <example lang="python">
        class MůjSeznam(list):
            ...
    </example>
    <note>
        Všimněte si dvojtečky – třída zavádí svůj vlastní blok a dokonce podobně jako funkce i svůj vlastní jmenný prostor!
    </note>
    <p>
        Identifikátor <code>MůjSeznam</code> nyní múžete používat místo (vestavěné) funkce <code>list</code> a vytvořená struktura se bude opravdu chovat jako seznam:
    </p>
    <example lang="python">
        >>> xs = MůjSeznam()
        >>> xs
        []
        >>> type(xs)
        &lt;class '__main__.MůjSeznam'&gt;
        >>> xs.append(123)
        >>> xs
        [123]
        >>> xs.pop()
        123
        >>> xs
        []
    </example>
    <p>
        Náš nový <em>MůjSeznam</em> se však zatím chová (prakticky) úplně stejně<sup>*</sup> jako vestavěný seznam <code>list</code>, duplikáty zatím neřeší.
    </p>
    <note>
        <sup>*</sup>V tomto případě dokonce tak moc, že jsme mohli rovnou napsat <code>MůjSeznam = list</code> a ušetřit si řádku kódu a drobné rozdíly ve vnitřní struktuře :-)
    </note>

</slide>
<slide title="Úprava poděděných vlastností">

    <p>
        Tělo třídy z <a href="?slajd=3">předchozího slajdu</a> je sugestivně prázdné pomocí objektu výpustky <code>...</code>. To naznačuje, že do něj můžeme něco napsat. Třebas definici funkce <code>append()</code>, která ale bude zahazovat duplikáty:
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            def append(self, prvek):
                if prvek not in self:
                    super().append(prvek)
    </example>
    <p>
        Než si kód vysvětlíme, podívejme se, jak se chová:
    </p>
    <example lang="python">
        >>> xs = MůjSeznam()
        >>> xs
        []
        >>> xs.append(123)
        >>> xs
        [123]
        >>> xs.append('abc')
        >>> xs
        [123, 'abc']
        >>> xs.append(123)
        >>> xs
        [123, 'abc']
    </example>
    <p>
        Tak, jak jsme chtěli – jako seznam a zahazuje duplikáty.
    </p>

</slide>
<slide title="Vysvětlení kódu">

    <p>
        Podívejme se znovu na kód:
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            def append(self, prvek):
                if prvek not in self:
                    super().append(prvek)
    </example>
    <p>
        Třída <em>MůjSeznam</em> podědila všechny vlastnosti vestavěného seznamu <code>list</code> s jedinou výjimkou – metoda <code>append()</code> pro přidání prvku se na ní chová jinak. Přitom:
    </p>
    <ul>
        <li>
            zavoláním <code>MůjSeznam()</code> vyrobíme tzv. <strong>instanci třídy</strong>, v našem případě konkrétního zástupce modifikovaného typu seznam;
        </li>
        <li>
            proměnná <em>self</em> v kódu zjevně odkazuje na tuto vyrobenou instanci seznamu (viz kód <code>if prvek not in self</code>);
        </li>
        <li>
            jelikož my sami přidat prvek do seznamu neumíme, delegujeme tuto práci na rodiče (<code>super()</code>), který to samozřejmě umí (metoda <em>append()</em> vestavěného typu <code>list</code>), jen tam prostě nepošleme všechny prvky (pouze neduplikáty).
        </li>
    </ul>
    <p>
        Jediná divná věc je, že námi nadefinovaná <strong>metoda</strong> (tak se říkám funkcím, které patří k nějaké konkrétní instanci či třídě) <em>append(self, prvek)</em> obsahuje dva parametry, přestože se nadále v kódu volá pouze s jedním jako <code>xs.append(123)</code>. Jak je to možné? <a href="?slajd=6">Viz dále.</a>
    </p>

</slide>
<slide title="Co je to „self“? I">

    <p>
        Kdyby autoři programovacího jazyka Python ho skutečně zamýšleli pro výuku, zařídili by, aby se předchozí kód psal například takto:
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            @instance_method
            def append(self, prvek):
                if prvek not in self:
                    super().append(prvek)
    </example>
    <p>
        Ani byste nemuseli vědět, že <code>@instance_method</code> před definicí funkce označuje tzv. <a href="/materialy/python/functions/decorators.xml"><strong>dekorátor</strong></a>, abyste zpozorněli a řekli si: „Aha, tahle funkce se asi nebude chovat úplně tak, jak bych na první pohled čekal!“ Pak už by úplně stačilo, aby vám někdo řekl (nebo jste si někde našli), že:
    </p>
    <blockquote>
        Metody instancí při svém volání <strong>dostanou automaticky na prvním místě jeden parametr navíc</strong>, a to právě odkaz na příslušnou instanci.
    </blockquote>
    <p>
        A od téhle chvíle dál byste si prostě pamatovali, že v Python'u při definici metody instance musíte na začátek parametrů připsat <code>self, </code> a bylo by vystaráno.
    </p>

</slide>
<slide title="Co je to „self“? II">

    <p>
        Bohužel v historii při vývoji Python'u došlo k <a href="" class="external">rozhodnutí</a>, že instanční metody potřebují lidi při vytváření tříd tak často, že jim ušetříme psaní a nebudeme je nijak označovat. Takže si holt musíte zapamatovat, že:
    </p>
    <blockquote>
        Funkce v těle třídy, <strong>pokud se mají chovat jako instanční metody</strong>, musí na prvním místě v seznamu parametrů navíc obsahovat referenci na instanci.
    </blockquote>
    <p>
        Že se této referenci říká zrovna <em>self</em> je sice velice výmluvné, ale kupodivu to není vyžadované (byť je to velmi doporučované). Zkuste si ji přejmenovat třebas na <em>this</em>:
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            def append(this, prvek):
                if prvek not in this:
                    super().append(prvek)
    </example>
    <p>
        Výsledek se bude chovat úplně stejně. Akorát ostatní pythoní programátoři vás asi nebudou mít moc rádi :-)
    </p>
    <p>
        PS: Popravdě většina ostatních programovacích jazyků podporujících OOP to píše v podstatě takto..
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            def append(prvek):
                if prvek not in this:
                    super().append(prvek)
    </example>
    <p>
        ..neboli reference na instanci se do metody explicitně nepředává jako jeden z jejích parametrů, ale „automagicky“ se objeví v těle metody pod klíčovým pojmenováním <code>this</code> (proto jsem ho také vybral). Takže jde spíš o to, jaká z variant se tvůrcům toho kterého jazyka víc líbila.
    </p>

</slide>
<slide title="Poznámka">

    <p>
        Na <a href="?slajd=7">předchozím slajdu</a> vás mohla zarazit věta <em>„pokud se mají chovat jako instanční metody“</em>. A znamená přesně to, co z ní vyplývá:
    </p>
    <blockquote>
        Metod ve třídách existuje více druhů a chovají se různě. (A také se různě zapisují.)
    </blockquote>
    <p>
        Postupně se s těmito dalšími druhy metod seznámíme.
    </p>

</slide>
<slide title="Motivační příklad 2">

    <p>
        Řekněme, že by vás zajímalo, kolik různých instancí vašeho upraveného seznamu už v programu používáte. Zjevně byste potřebovali ve chvíli, kdy vyrábíte novou instanci, někam zaznamenat, že vám právě jedna přibyla. Na to potřebujete dvě věci:
    </p>
    <ul>
        <li>
            „nabourat“ se do procesu tvorby instance, abyste mohli provést inkrementaci příslušného čítače;
        </li>
        <li>
            umístit tento čítač na takové místo, odkud ho uvidí (stejný!) všechny instance.
        </li>
    </ul>
    <p>
        Druhý bod splníte tím, že čítač (proměnnou) zavedete v těle třídy, čímž bude dostupný jako prvek jejího <strong>jmenného prostoru</strong>. První bod pak umožňuje Python přepsáním (v definici třídy) speciální funkce jménem <code>__init__()</code> (skutečně jsou na začátku a na konci dvě podtržítka!), která se volá při zavádění nových instancí.
    </p>
    <p>
        PS: Funkcím, či vlastně metodám, jejichž jméno začíná a končí na dvě podtržítka, se v Python'u říká <strong>magické metody</strong>. Je to běžné používaný <em>terminus technicus</em> a možná byl vybrán proto, <a href="magic.xml">co všechno s jejich pomocí můžete zařídit</a>.
    </p>

</slide>
<slide title="Magická metoda „__init__()“">

    <p>
        Příslušný kód může vypadat například takto:
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            čítač_instancí = 0
            
            def __init__(self, *první_prvek):
                MůjSeznam.čítač_instancí += 1
                super().__init__(*první_prvek)
    </example>
    <p>
        Jeho funkcionalita je celkem průhledná:
    </p>
    <ul>
        <li>
            proměnná <em>čítač_instancí</em> je definována v těle třídy, patří tudíž do jejího <em>jmenného prostoru</em> a je tak dostupná – podobně jako u modulů – pomocí tečkové notace, tj. <code>MůjSeznam.čítač_instancí</code>;
        </li>
        <li>
            funkce <code>__init__()</code> se chová jako každá jiná instanční metoda, tzn. dostává jako první parametr referenci na odpovídající instanci a funkce <code>super()</code> získá odkaz na rodiče naší třídy, na němž pak zavolá jeho metodu <code>__init__()</code> a provede inicializaci našeho upraveného seznamu prvním prvkem.
        </li>
    </ul>
    <p>
        Co možná není na první pohled průhledné, je použití operátoru hvězdička <code>*</code> v kódu, ale to až <a href="?slajd=12">později</a>.
    </p>
    <note>
        Že je v době volání metody <code>__init__()</code> už k dispozici příslušný prázdný seznam, do kterého může rodič přidat první prvek, zajišťuje metoda <code>__new__()</code> ve spolupráci s funkcí <code>type()</code>, ale to už bychom <a href="meta.xml">zabředli do vnitřností OOP v Python'u</a> více, než je zdrávo.
    </note>
    <p>
        PS: Metodám, které zařídí vyrobení nové instance a její případné naplnění vstupními prvky, se obecně říká <strong>konstruktory</strong>. Budu tak nadále říkat i naší metodě <code>__init__()</code>.
    </p>

</slide>
<slide title="Ověření funkce kódu">

    <p>
        V úplnosti náš kód zatím vypadá takto..
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            čítač_instancí = 0
            
            def __init__(self, *první_prvek):
                MůjSeznam.čítač_instancí += 1
                super().__init__(*první_prvek)
            
            def append(self, prvek):
                if prvek not in self:
                    super().append(prvek)
    </example>
    <p>
        ..a chová se přesně tak, jak jsme chtěli:
    </p>
    <example lang="python">
        >>> xs1 = MůjSeznam('abc')
        >>> xs1
        ['a', 'b', 'c']
        >>> xs1.čítač_instancí
        1

        >>> xs2 = MůjSeznam()
        >>> xs2
        []
        >>> xs2.čítač_instancí
        2
        >>> xs1.čítač_instancí
        2
    </example>
    <p>
    </p>

</slide>
<slide title="Poznámka k použití „*“">

    <p>
        Operátor hvězdičky <code>*</code> jsme museli do metody <code>__init__()</code> v <a href="?slajd=10">předchozím kódu</a> přidat, aby se náš upravený seznam choval jako vestavěný i při inicializaci, tzn. bez parametru vyrobil prázdný seznam a s parametrem ho v případě potřeby rozdělil na jednotlivé prvky:
    </p>
    <example lang="python">
        >>> list()
        []
        >>> MůjSeznam()
        []
        >>> list('abc')
        ['a', 'b', 'c']
        >>> MůjSeznam('abc')
        ['a', 'b', 'c']
    </example>
    <p>
        Vysvětlení:
    </p>
    <ul>
        <li>
            Pokud bychom nenapsali hvězdičku v hlavičce, tj. <code>__init__(self, první_prvek)</code>, byl by právě jeden parametr vždy vyžadován a konstruktor by bez něj nešel zavolat:
            <example lang="python">
                >>> MůjSeznam('abc')
                ['a', 'b', 'c']

                >>> MůjSeznam()
                Traceback (most recent call last):
                  File "&lt;pyshell#65>", line 1, in &lt;module>
                    MůjSeznam()
                    ~~~~~~~~~^^
                TypeError: MůjSeznam.__init__() missing 1 required positional argument: 'první_prvek'
            </example>
        </li>
        <li>
            Jelikož hvězdička v hlavičce způsobí předání případného parametru jako jednice (n-tice s jedním prvkem), následné zavolání <code>super().__init__(první_prvek)</code> bez hvězdičky by přeposlalo dál tuto jednici a výsledek by byl úplně jiný:
            <example lang="python">
                >>> MůjSeznam()
                []
                >>> MůjSeznam('abc')
                ['abc']
            </example>
            Hvězdička způsobí rozbalení n-tice na jednotlivé prvky (zde tedy jeden) a kód se chová správně.
        </li>
    </ul>
    <p>
        PS: Hvězdička samozřejmě umožní zadat libovolný počet pozičních parametrů, což v konstruktoru seznamu není dovoleno, ale o to už se postará metoda <code>__init__()</code> rodiče, tedy vestavěného typu seznam.
    </p>

</slide>
<slide title="Třídní metody „@classmethod“">

    <p>
        Kód <code>MůjSeznam.čítač_instancí += 1</code> samozřejmě není moc hezký – pokud bychom kdo ví proč později třídu přejmenovali, museli bychom změnit i toto místo, což není nejšťastnější. Python nepřekvapivě umožňuje oddělit referenci na třídu podobným způsobem, jako jsme oddělili referenci na instanci pomocí prvního parametru <em>self</em>, jen se mu v tomto případě doporučuje říkat <em>cls</em> (od <em>class</em>):
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            čítač_instancí = 0
            
            def __init__(self, *první_prvek):
                self.inkrementuj_čítač_instancí()
                super().__init__(*první_prvek)
            
            @classmethod
            def inkrementuj_čítač_instancí(cls):
                cls.čítač_instancí += 1
    </example>
    <p>
        <code>@classmethod</code> je tedy, jak <a href="?slajd=6">už víme</a>, tzv. <a href="/materialy/python/functions/decorators.xml"><strong>dekorátor</strong></a>, a pokud je neznáte, stačí si pamatovat, že umí upravit, <strong>jak se jím odekorovaná funkce konkrétně chová</strong>. V našem případě zjevně takto označeným metodám podstrčí na prvním místě parametrů referenci na příslušnou třídu:
    </p>
    <example lang="python">
        >>> xs1 = MůjSeznam('abc')
        >>> xs1
        ['a', 'b', 'c']
        >>> xs1.čítač_instancí
        1

        >>> xs2 = MůjSeznam()
        >>> xs2
        []
        >>> xs2.čítač_instancí
        2
        >>> xs1.čítač_instancí
        2
    </example>

</slide>
<slide title="Statické metody „@staticmethod“">

    <p>
        Kromě toho existuje ještě jeden dekorátor pro metody ve třídách – <code>@staticmethod</code>:
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            @staticmethod
            def pomocná_funkce(a, b):
                print(f'{a} + {b} = {a + b}')
    </example>
    <p>
        Nepřekvapivě zařizuje, že funkce se chovají tak, jak jsou napsané, ať už je voláte na instanci nebo přímo ve jmenném prostoru třídy:
    </p>
    <example lang="python">
        >>> MůjSeznam.pomocná_funkce(1, 2)
        1 + 2 = 3

        >>> xs = MůjSeznam()
        >>> xs.pomocná_funkce(1, 2)
        1 + 2 = 3
    </example>
    <p>
        V tomto případě tedy dekorátor <code>@staticmethod</code> naopak způsobí, že funkce definovaná ve jmenném prostoru třídy se chová pořád stejně, tj. nedostává na prvním místě parametrů podstrčenou žádnou referenci navíc.
    </p>

</slide>
<slide title="Shrnutí">

    <p>
        Celkově tedy Python rozeznává u tříd tři druhy metod:
    </p>
    <ul>
        <li>
            <strong>instanční</strong> – funkce ve jmenném prostoru třídy nijak označené, při volání dostanou navíc na prvním místě referenci na příslušnou instanci (která se tradičně označuje <em>self</em>)
            <example lang="python">
                def instanční_metoda(self, ...):
            </example>
            <note>
                A nemůžu si pomoct se zase neozvat, že by bývalo bylo mnohem lepší, kdyby byly označené třebas dekorátorem <code>@instancemethod</code>…
            </note>
        </li>
        <li>
            <strong>třídní</strong> – funkce ve jmenném prostoru třídy označené dekorátorem <code>@classmethod</code>, při volání dostanou navíc na prvním místě referenci na odpovídající třídu (která se tradičně označuje <em>cls</em>)
            <example lang="python">
                @classmethod
                def třídní_metoda(cls, ...):
            </example>
        </li>
        <li>
            <strong>statické</strong> – funkce ve jmenném prostoru třídy označené dekorátorem <code>@staticmethod</code>, při volání se chovají očekávaně (tj. Python do jejich parametrů nic nepřidává)
            <example lang="python">
                @staticmethod
                def statická_metoda(...):
            </example>
            <note>
                No a tyhle zase žádný dekorátor mít nemusely, když se chovají jako běžné funkce…
            </note>
        </li>
    </ul>

</slide>
<slide title="Viditelnost jmen na objektu – „dir()“">

    <p>
        Celkově naše třída nyní vypadá takto:
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            čítač_instancí = 0
            
            def __init__(self, *první_prvek):
                self.inkrementuj_čítač_instancí()
                super().__init__(*první_prvek)
            
            @classmethod
            def inkrementuj_čítač_instancí(cls):
                cls.čítač_instancí += 1
            
            def append(self, prvek):
                if prvek not in self:
                    super().append(prvek)
            
            @staticmethod
            def pomocná_funkce(a, b):
                print(f'{a} + {b} = {a + b}')
    </example>
    <p>
        Všechny objekty, které jsme uvnitř ní nadefinovali, jsou vidět v jejím jmenném prostoru při výpisu funkcí <code>dir()</code>:
    </p>
    <example lang="python">
        >>> dir(MůjSeznam)
        [..., 'append', 'clear', 'copy', 'count', 'extend', 'index',
         'inkrementuj_čítač_instancí', 'insert', 'pomocná_funkce', 'pop',
         'remove', 'reverse', 'sort', 'čítač_instancí']
    </example>
    <note>
        A výpis pro instanci <code>dir(MůjSeznam())</code> je úplně stejný.
    </note>

</slide>
<slide title="„Magické metody“">

    <p>
        Z výpisu atributů na <a href="?slajd=16">předchozím slajdu</a> jsem pro přehlednost vynechal <strong>magické metody</strong>, tj. všechny ty, <strong>jejichž jméno začíná a končí právě na dvě podtržítka</strong>.
    </p>
    <p>
        Je jich tam totiž strašně moc :-) Samozřejmě mezi nimi naleznete náš nyní již <a href="?slajd=10">starý známý <code>__init__</code></a>, dále metody, které vám asi něco připomenou (jako např. <code>__add__</code>, <code>__contains__</code> nebo <code>__len__</code>), ale především hromadu dalších, které vám zatím asi nic neříkají. Ale když si ukážeme následující kód..
    </p>
    <example lang="python">
        >>> xs = MůjSeznam()
        >>> xs
        []
        >>> len(xs)
        0
        >>> xs.append(123)
        >>> len(xs)
        1
        >>> xs.__len__()
        1
    </example>
    <p>
        ..začnete asi tušit, že <em>magické metody</em> vlastně <strong>řídí konkrétní chování objektů</strong> (tříd). Nejenom že spousta vestavěných funkcí z globálního jmenného prostoru vlastně „pouze“ volá odpovídající magické metody na příslušných objektech (namátkou kromě <code>len()</code> také třeba <code>str()</code> nebo <code>format()</code>), ale i většina operací s objekty se překládá na vhodné volání jejich magických metod (např. volání <code>x &lt; y</code> ve skutečnosti způsobí zavolání <code>x.__lt__(y)</code> a podobně a smyčka <em>for-in</em> zase funguje jen a pouze díky přítomnosti magické metody <code>__iter__</code>).
    </p>
    <p>
        Protože magické metody v Python'u opravdu můžou snad úplně za všechno, tady už to stačí a další najdete v několika samostatných přednáškách: <a href="/materialy/python/files/with.xml">kontext <em>with-as</em></a>, <a href="/materialy/python/sorting/advanced.xml">třídění a porovnávání</a>, <a href="/materialy/python/iterators/iterators.xml">iterátory a cyklus <em>for-in</em></a>, <a href="magic.xml">další magické metody</a>.
    </p>
    <note>
        PS: Na naší ukázkové třídě nejsou sice magické metody úplně stejné jako na rodiči (tj. seznamu), ale rozdíl je pro náš výklad nepodstatný – třída <em>MůjSeznam</em> obsahuje oproti vestavěnému seznamu navíc pouze <code>__dict__</code>, <code>__firstlineno__</code>, <code>__module__</code>, <code>__static_attributes__</code> a <code>__weakref__</code>.
    </note>

</slide>
<slide title="Poznámka – „privátní“ metody">

    <p>
        Většina programovacích jazyků podporujících principy OOP zahrnuje ještě koncept tzv. <strong>privátních</strong> metod (či atributů). Takové jsou určeny pouze pro vnitřní potřeby třídy a mimo ni nejsou viditelné (a pochopitelně ani volatelné).
    </p>
    <p>
        Python trochu překvapivě <em>privátní atributy</em> (včetně metod) nemá. Pokud je potřebujete, místo neexistujícího dekorátoru (například) <code>@privatemethod</code> se používá přidání <strong>dvou podtržítek před vlastní název atributu</strong> (a nejvýše jednoho podtržítka za něj; jinak byste zavedli <em>magický název</em>):
    </p>
    <example lang="python">
        class MůjSeznam(list):
            
            __schovaná_proměnná = 'Baf!'
            
            @staticmethod
            def __schovaná_statická_metoda(a, b):
                print(f'{a} - {b} = {a - b}')
    </example>
    <p>
        Nyní už atributy <code>__schovaná_proměnná</code> a <code>__schovaná_statická_metoda</code> pod jejich názvy ve výpisu funkce <em>dir()</em> nenajdete. Tedy alespoň ne přímo, protože ve skutečnosti tam jsou, pouze s trošku pozměněnými jmény:
    </p>
    <example lang="python">
        ['_MůjSeznam__schovaná_proměnná', '_MůjSeznam__schovaná_statická_metoda', ...]
    </example>
    <p>
        Jak vidíte, došlo k úpravě jejich názvů ve formátu <code>_JménoTřídy__JménoAtributu</code>. Takže kdo to ví, i takto „schované“ atributy ve třídě najde. Nicméně jako na mnoha jiných místech v Python'u, která se řídí dohodnutým zvykem, se všichni tváří, že tam nejsou ^_~
    </p>

</slide>
<slide title="Třídy bez rodičů I">

    <p>
        Kromě podědění vlastností nějaké vestavěné – nebo námi vytvořené! – třídy, umožňuje Python vyrobit i třídy zdánlivě bez rodiče:
    </p>
    <example lang="python">
        class MojeTřída:
            ...
    </example>
    <p>
        Ve skutečnosti i takovéto třídy mají svého rodiče – je jím univerzální prapředek všech objektů v Python'u <code>object</code>, jak ukazuje následující kód vypisující „rodný strom“ třídy:
    </p>
    <example lang="python">
        >>> inspect.getmro(MojeTřída)
        (&lt;class '__main__.MojeTřída'>, &lt;class 'object'>)
    </example>
    <p>
        Jedná se tedy v podstatě pouze o zkratku v zápisu, ale zrovna v tomto místě užitečnou (narozdíl od oněch nešťastných instančních metod).
    </p>

</slide>
<slide title="Třídy bez rodičů II">

    <p>
        Nabízí se otázka, k čemu vlastně takovéto obecné třídy, které se nesnaží napodobit nějaký vestavěný objekt, jsou. Protože <strong>třídy si otevírají vlastní kontext</strong>, většinou se používají na zapouzdření dat (i funkcí) do příslušného <strong>jmenného prostoru třídy</strong>..
    </p>
    <example lang="python">
        class MojeTřída:
            
            a = 333
            b = 'Ahoj!'
            
            def fn(x):
                return x + x
    </example>
    <p>
        ..odkud se pak dají pomocí klasické tečkové notace vyvolat:
    </p>
    <example lang="python">
        >>> MojeTřída.a
        333
        >>> MojeTřída.b
        'Ahoj!'
        >>> MojeTřída.fn(3)
        6
        >>> MojeTřída.fn('abc')
        'abcabc'
    </example>
    <p>
        Samozřejmě můžete takovouto třídu použít i jako konstruktor, ale neoznačené funkce se pak stanou instančními metodami a vůbec všechno tohle okolo.
    </p>

</slide>
<slide title="Další čtení">

    <p>
        Jelikož všechno v Python'u je objekt (tedy vlastně původně nějaká třída), nepřekvapí, že k danému tématu se toho dá sepsat opravdu hodně. Zde je uspořádaný výběr některých témat, která jsem za ty roky učení vyprodukoval:
    </p>
    <h3>úvod pro ne-OOP-programátory</h3>
    <ol>
        <li><a href="overview.xml">obecný přehled</a></li>
        <li><a href="basics.xml">úvod</a> (zavedení, atributy, inicializace, dědičnost…)</li>
    </ol>
    <h3>aplikace</h3>
    <ul>
        <li><a href="/materialy/python/iterators/iterators.xml">iterátory</a></li>
        <li><a href="/materialy/python/sorting/advanced.xml">třídění a porovnávání</a></li>
        <li>kontextový manažer <a href="/materialy/python/files/with.xml"><em>with</em></a></li>
        <li><a href="/materialy/python/numpy/slices.xml">Výřezy pro pokročilé</a> aneb <em>„Povídání nejen o vícerozměrných polích“</em></li>
        <li>dekorátory třídním způsobem</li>
    </ul>
    <h3>všechno ostatní</h3>
    <ol>
        <li><a href="oop.xml">úvod pro OOP-programátory</a></li>
        <li>funkce <a href="super.xml"><code>super()</code></a></li>
        <li><a href="introspection.xml">introspekce</a> (včetně modulu <a href="/materialy/python/modules/inspect.xml"><code>inspect</code></a>)</li>
        <li><a href="descriptors.xml">deskriptory</a> (aka <em>getters&amp;setters</em>)</li>
        <li><a href="magic.xml">„magické“ metody</a></li>
        <li><a href="decorators.xml">dekorátory tříd</a></li>
        <li><a href="meta.xml">Třídy pro pokročilé</a> aneb <em>„Na slovíčko o metatřídách“</em></li>
        <li><a href="ABCs.xml"><em>ABCs</em></a> (<em>abstract base classes</em>)</li>
    </ol>

    <p>
    </p>

</slide>


</lecture>
