<?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>Objekty pro pokročilé</title>
  <date>2016-04-xx</date>
  <link><!--a href="http://vyuka.ookami.cz" rel="external">http://vyuka.ookami.cz</a--></link>
</meta>
<!--
  „“–
  ↵ aneb &#x21B5; aneb \r aneb CR aneb CarriageReturn
-->
<!--
    ~ dekoratory trid
      http://www.python.org/dev/peps/pep-3129
-->


<slide title="Úvod">

    <p>
        Python umožňuje provádět spoustu věcí jinde nemyslitelných. Koneckonců, všechno v Python'u je přece objekt, se kterým si můžeme dělat (téměř) všechno, co chceme:
    </p>
    <example lang="python">
>>> def fn():
...   pass
... 
>>> dir(fn)
['__annotations__', '__call__', '__class__', '__closure__', '__code__',
 '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
'__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__',
'__hash__', '__init__', '__kwdefaults__', '__le__', '__lt__', '__module__',
'__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']

>>> fn.haf = 'Já jsem funkce!'
>>> fn.haf
'Já jsem funkce!'
    </example>
    <p>
        Ale jak už to tak bývá, mocné prostředky se dají také velice snadno zneužít. Takže pozor na ně ^_~
    </p>

</slide>
<slide title="Metatřídy">

    <p>
        Stejně jako jsou objekty konkrétními instancemi svých tříd (nebo vlastně také typů), <strong>jsou třídy instancemi svých metatříd</strong>:
    </p>
    <blockquote>
        Metatřídy jsou volatelné objekty, které slouží ke tvorbě tříd. Jejich návratovou hodnotou je tudíž volatelný objekt, který pak následně slouží jako konstruktor (nějaké) třídy.
    </blockquote>
    <note>
        V jiných jazycích by se pythoním metatřídám asi říkalo <em>class factory</em>.
    </note>
    <p>
        Spoustu věcí, na které se dříve metatřídy především používaly, dnes už dokážou obstarat <a href="decorators.xml">dekorátory tříd</a>, které jsou ovšem podstatně mladšího data. Ale narozdíl od nich <strong>se metatřídy dědí</strong> (jsou to koneckonců vlastně „obyčejné“ typy, něco jako třídy na druhou), takže <strong>metatřídou „odekorované“ chování nových tříd mohou podědit i jejich potomci</strong>, což samozřejmě dekorátory nedokážou.
    </p>

</slide>
<slide title="Jak je vytvořena instance třídy">

    <p>
        Než se začneme topit v metatřídách, začněmež od něčeho jednoduššího – <strong>zavedením nové instance třídy</strong>. Tento proces je (alespoň od dob zavedení „nových“ objektů ve dvojkovém Python'u) ve skutečnosti dvoustupňový – nejdříve je vytvořen objekt samotný, aby byl následně předán ke svému naplnění vstupními parametry vlastního konstruktoru:
    </p>
    <ol>
        <li>
            volající kontext si vyžádá zavedení nové instance třídy;
        </li>
        <li>
            tato nová instance třídy je vytvořena zavoláním metody <code>__new__(cls[, …])</code> požadované třídy…
            <note>
                Kde <em>cls</em> je reference na tuto třídu.
            </note>
        </li>
        <li>
            …a následně je její jmenný prostor patřičně upraven voláním metody <code>__init__(self[, …])</code> této třídy;
            <note>
                Kde <em>self</em> je reference na nový objekt čerstvě vytvořený v předchozím kroku.
            </note>
        </li>
        <li>
            takto vyrobený a upravený objekt instance je poté vrácen žadateli.
            <note>Jinými slovy: Je přidán do jmenného prostoru jeho kontextu.</note>
        </li>
    </ol>
    <p>
        Na tomto procesu je zajímavé, že pythoní programátoři mají možnost se v obou krocích podle svého (zne)uvážení přehrabovat, protože jsou oba dostupné pod svými vlastními „magickými“ metodami.
    </p>

</slide>
<slide title="Co dělá „__new__()“">

    <p>
        Zatímco konstruktor instance třídy <a href="/materialy/python/objects/oop.xml?slajd=3"><code>__init__()</code></a> znají všichni jako své boty, vlastního stvořitele této instance <code>__new__()</code> vidí asi poprvé. Jde o to, že <em>__init__()</em> jako svůj první parametr (automaticky) dostává odkaz na právě zaváděnou instanci, takže mu ji někdo jiný musí nejdříve připravit. A tím někým je právě <em>__new__()</em>.
    </p>
    <p>
        Vzhledem k tomu, že <em>__new__()</em> tuto novou instanci přivede k existenci, nepřekvapí snad, že ji také vrací. Přitom nejsnažším způsobem, jak zajistit vytvoření správného typu objektu, je přenechat práci na svém rodiči (ať už je jím kdo chce):
    </p>
    <example lang="python">
>>> class Pokus:
...     def __new__(cls):
...         print('__new__:', cls)
...         return super().__new__(cls)
...     def __init__(self):
...         print('__init__:', self)

>>> p = Pokus()
__new__: &lt;class '__main__.Pokus'>
__init__: &lt;__main__.Pokus object at 0x0110DC50>
    </example>
    <p>
        PS: Nenechte se rozhodit tím, že <em>__new__(cls)</em> dostává na vstupu automaticky referenci na svoji vlastní třídu. Ve skutečnosti je to statická metoda třídy (schválně si před ni zkuste připsat <em>@staticmethod</em>) s pevně určeným prvním parametrem. Navíc je to celé v interpretru zadrátovaná vlastnost, takže se prostě píše takhle. Netuším proč.
    </p>

</slide>
<slide title="O důležitosti vraceného typu">

    <p>
        Metodu <em>__new__()</em> si pochopitelně můžete upravit k obrazu svému, ale jakmile se vám podaří nevrátit z ní referenci na správný typ objektu (zde tedy <em>Pokus</em>), odpovídající <em>__init__()</em> se pochopitelně nezavolá (protože <a href="oop.xml?slajd=4">se volá na příslušné třídě</a>). Jako třeba když nevrátíte nic:
    </p>
    <example lang="python">
>>> class Pokus:
...     def __new__(cls):
...         print('__new__:', cls)
...     def __init__(self):
...         print('__init__:', self)

>>> p = Pokus()
__new__: &lt;class '__main__.Pokus'>
    </example>
    <p>
        Samozřejmě často není důvod vůbec <em>__init__()</em> volat, takže to koneckonců může být i žádané chování. Ale když zrovna není, tak víte, kam se podívat na pravděpodobného viníka.
    </p>

</slide>
<slide title="Od „__new__()“ k metatřídám">

    <p>
        Při tvorbě vlastních metatříd <strong>typicky musíte <em>__new__()</em> přepsat, protože</strong> – jak už nyní víme – <strong>to je právě to místo, které je zodpovědné za tvorbu nových tříd</strong>.
    </p>
    <note>
        A pak ještě při dědění z neproměnných vestavěných typů: Má-li býti totiž nově vytvořený objekt co k čemu, nemůže to být pouhá reference na původní neproměnný typ, kterou jedinou můžete získat z <em>__init__()</em>'u – neproměnný typ je totiž od toho neproměnný, že ho nemůžete zaměnit za něco jiného ^_~
    </note>
    <p>
        Kdyby ovšem přepsání kroku <em>__new__()</em> bylo to jediné, co by metatřídy dělaly, nikdo by je nepotřeboval a všichni by háčkovali obyčejné třídy (ne že by se metatřídy od tříd nějak extra lišily, že).
    </p>
    <p>
        Ve skutečnosti umí metatřídy kompletně předepsat (a přepsat) způsob, jakým jsou za běhu (tedy dynamicky) vyrobeny nové typy (nebo – chcete-li – třídy). Na což mimochodem je – asi trochu překvapivě vzhledem ke svému běžnému použití – specializována funkce <em>type()</em>.
    </p>

</slide>
<slide title="Jak je vytvořena třída">

    <p>
        <strong>Vytvoření nové třídy</strong> je vcelku náročný proces. I když do něj nebudete většinou potřebovat (natožpak chtít) zasahovat, vyplatí se o něm alespoň něco vědět. V podstatě jsou při zavádění nové třídy provedeny následující kroky, a to v uvedeném pořadí:
    </p>
    <ol>
        <li>
            volající kontext si vyžádá zavedení nové třídy;
        </li>
        <li>
            je nalezena nejvhodnější metatřída jako základ nově zaváděné třídy;
            <note>Ve spoustě případů je jí právě <em>type()</em>, ale programátor může samozřejmě předepsat jinou.</note>
        </li>
        <li>
            je zaveden nový jmenný prostor třídy (a to buď jako prázdný slovník zavoláním konstruktoru <em>dict()</em> nebo – je-li k dispozici – voláním metody <em>__prepare__</em> na metatřídě);
        </li>
        <li>
            je vykonáno tělo definice třídy (čímž dojde k naplnění jejího jmenného prostoru, tedy zavedení atributů třídy);
        </li>
        <li>
            konstruktor metatřídy (spolu se jménem zaváděné třídy a případnými rodiči a vstupními parametry) je použit k vytvoření „instance“ nové třídy;
            <note>Tímto konstruktorem bývá velmi často tedy <em>type()</em>. Přitom vrácený objekt nové třídy je právě ten, na který odkazuje metoda <em>super()</em> (bez parametrů!) z jejích instancí.</note>
        </li>
        <li>
            obsahuje-li definice třídy dekorátory, je jim nově vytvořená instance „předhozena“, aby ji upravily k obrazu svému;
        </li>
        <li>
            výsledný objekt je namapován do jmenného prostoru volajícího kontextu.
        </li>
    </ol>
    <p>
        Při běžném programování je pro nás asi nejdůležitější vědět, v jaké chvíli je vykonáván krok dekorátorů (a i tak se dekorování tříd nedělá moc často). Při „těžkém háčkování“ pomocí metaprogramování pak asi nalezení odpovídající metatřídy (což není triviální záležitost) a její aplikace.
    </p>

</slide>
<slide title="„type()“ jako metatřída">

    <p>
        Nejdůležitější metatřídu ze všech tedy všichni dobře znají – je jí totiž (jak už jsem říkal možná trochu překvapivě) stará dobrá známá funkce <em>type()</em>.
    </p>
    <p>
        <a href="/materialy/python/objects/introspection.xml?slajd=14">Jak už víme</a>, běžně slouží funkce <code>type(OBJEKT)</code> ke zjišťování typu OBJEKTu. Návratová hodnota přitom většinou odpovídá atributu <code>OBJEKT.__class__</code>.
    </p>
    <p>
        Se třemi vstupními argumenty se však <em>type</em> promění na <strong>konstruktor tříd aneb tzv. metatřídu</strong>, což je popravdě skrytě její zdaleka nejčastější a nejdůležitější použití:
    </p>
    <!--  třída = type(name, bases, dict)  -->
    <pre>
        třída = type(JménoTřídy, RodičeTřídy, JmennýProstorTřídy)
    </pre>
    <p>
        Přitom platí následující korespondence mezi atributy a výslednou třídou:
    </p>
    <ul>
        <li>
            <em>JménoTřídy</em> – jméno třídy; odpovídá <code>třída.__name__</code>
        </li>
        <li>
            <em>RodičeTřídy</em> – n-tice rodičovských tříd; odpovídá <code>třída.__bases__</code>
        </li>
        <li>
            <em>JmennýProstorTřídy</em> – slovník jmenného prostoru třídy; odpovídá <code>třída.__dict__</code>
        </li>
    </ul>
    <p>
        Pro obecné metatřídy platí vlastně to samé – jejich odpovědností je <strong>převzít jméno třídy, seznam rodičů a slovník parametrů budoucí třídy a z těchto tří složek onu třídu vytvořit</strong>.
    </p>

</slide>
<slide title="„type()“ – příklad">

    <example lang="python">
>>> X = type('X', (object,), dict(a=1))
>>> X
&lt;class '__main__.X'>
>>> X.__class__
&lt;class 'type'>
>>> dir(X)
['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__',
 '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__',
 '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
 '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
 '__weakref__', 'a']
>>> X.a
1

>>> x = X()
>>> x
&lt;__main__.X object at 0x00DCB890>
>>> x.__class__
&lt;class '__main__.X'>
>>> dir(x)
['__class__', '__delattr__', '__dict__', '__doc__', '__eq__', '__format__',
 '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__',
 '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__',
 '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
 '__weakref__', 'a']
>>> x.a
1
    </example>

</slide>
<slide title="Jak fungují metatřídy? I">

    <p>
        Z hlediska programátora fungují metatřídy celkem neviditelně – pokud si použití nějaké výslovně nevyžádá, odehrává se veškerá metatřídní „magie“ mimo jeho dohled: Při zavádění nové třídy se prostě zavolá nejvhodnější dostupný konstruktor, ve většině případů právě funkce <em>type()</em>, který požadovanou třídu vyrobí a vrátí nám ji.
    </p>
    <p>
        Pokud chceme použít nějakou specifickou metatřídu, můžeme si ji při zavádění nové třídy vyžádat pomocí atributu konstruktoru <em>metaclass</em>:
    </p>
    <example lang="python">
        class Třída(metaclass=Metatřída):
            pass
    </example>
    <p>
        Tahle zdánlivě nevinná operace samozřejmě vyžaduje, aby konstruktor <em>Metatřída</em> existoval a choval se tak, jak se spořádaná metatřída chovat má. A to už taková švanda není. V dalším se tomu všemu podíváme na zoubek.
    </p>

</slide>
<slide title="Jak fungují metatřídy? II">

    <p class="enumerate">
        Bez metatřídy napíšeme..
    </p>
    <example lang="python">
        class Třída:
            pass
    </example>
    <p>
        ..a zavolá se:
    </p>
    <example lang="python">
        Třída = type(Jméno, Rodiče, JmennýProstor)
    </example>

    <p class="enumerate">
        S metatřídou napíšeme..
    </p>
    <example lang="python">
        class Třída(metaclass=Metatřída):
            pass
    </example>
    <p>
        ..a zavolá se (alespoň v typickém případě):
    </p>
    <example lang="python">
        Třída = Metatřída(Jméno, Rodiče, JmennýProstor)

        # což odpovídá volání
        Třída = Metatřída.__new__(Metatřída, Jméno, Rodiče, JmennýProstor)
        Metatřída.__init__(Třída, Jméno, Rodiče, JmennýProstor)
    </example>
    <p>
        PS: V reálném případě stačí, aby nějaký předek byl založen na metatřídě – Python pak příslušnou volací sekvenci dohledá sám.
    </p>

</slide>
<slide title="Jak napsat metatřídu">

    <p class="enumerate">
        Z praktického hlediska jsou metatřídy obyčejné třídy, na kterých ale definujete „magickou“ metodu <em>__new__()</em>, která je právě volána při „výrobě“ instancí tříd, případně <em>__init__()</em>, která je volána při naplnění nově vytvořené instance daty.
    </p>
    <note>
        A to buď obě najednou nebo aspoň jednu z nich. Častější je zřejmě varianta pouze s <em>__new__()</em>.
    </note>
    <p>
        Stejně tak dobře můžeme ještě <a href="?slajd=19">přepsat metodu <em>__call__()</em></a>, která se volá při zavádění instancí, čímž můžeme mimo jiné odseparovat, co třída dělá, od toho, jak je vytvořena.
    </p>
    <p class="enumerate">
        Pokud nadefinujete také metodu <em>__prepare__()</em>, bude tato použita při přípravě jmenného prostoru třídy (jinak se použije běžný pythoní slovník <em>dict()</em>).
    </p>

</slide>
<slide title="Příklad I">

    <p>
        Příklad (upravený z dokumentace) bude určitě názornější. Připravme si následující metatřídu (která pro zajímavost definuje jak <em>__new__()</em>, tak i <em>__prepare__()</em>):
    </p>
    <example lang="python">
class OrderedClass(type):
    
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        print('__prepare__:', metacls, name, bases, kwargs)
        return collections.OrderedDict()

    def __new__(cls, name, bases, namespace, **kwargs):
        print('__new__:', cls, name, bases, namespace, kwargs)
        result = super().__new__(cls, name, bases, dict(namespace))
        result.members = tuple(namespace)
        return result
    </example>

</slide>
<slide title="Příklad IIa">

    <p>
        Pokud třídu <em>OrderedClass</em> použijeme jako metatřídu, stane se přesně to, co bychom očekávali:
    </p>
    <example lang="python">
>>> class A(metaclass=OrderedClass):
...     pass
__prepare__: &lt;class '__main__.OrderedClass'> A () {}
__new__: &lt;class '__main__.OrderedClass'> A () OrderedDict([
            ('__module__', '__main__'),
            ('__qualname__', 'A')
         ]) {}
    </example>
    <p>
        Uvedená metatřída byla použita jako konstruktor <strong>ke tvorbě nové třídy</strong>, tedy vlastně <em>instance metatřídy</em>, jak dokazuje:
    </p>
    <example lang="python">
>>> isinstance(A, OrderedClass)
 True
>>> A.__class__
 &lt;class '__main__.OrderedClass'>
>>> issubclass(A, OrderedClass)
 False
    </example>
    <p>
        Byly proto zavolány obě dvě metody v uvedeném pořadí – nejdříve <code>__prepare__()</code> pro zavedení upraveného jmenného prostoru budoucí třídy a následně <code>__new__()</code> pro vlastní zavedení této třídy.
    </p>

</slide>
<slide title="Příklad IIb">

    <p>
        Jestě pro zajímavost – pro instanci třídy <em>A</em> platí:
    </p>
    <example lang="python">
>>> a = A()

>>> a
 &lt;__main__.A object at 0x0111C6D0>
>>> a.__class__
 &lt;class '__main__.A'>

>>> isinstance(a, A)
 True
>>> isinstance(a, OrderedClass)
 False
    </example>

</slide>
<slide title="Příklad III">

    <p>
        Protože jsme uvedenou metatřídu zaváděli proto, abychom získali třídy, které si budou pamatovat pořadí svých atributů (zde konkrétně za pomoci atributu <em>members</em>), vyzkoušejme ještě tuto funkcionalitu:
    </p>
    <example lang="python">
>>> class A(metaclass=OrderedClass):
...     def jedna(self): pass
...     def dva(self): pass
...     def tři(self): pass
...     def čtyři(self): pass
__prepare__: &lt;class '__main__.OrderedClass'> A () {}
__new__: &lt;class '__main__.OrderedClass'> A () OrderedDict([
            ('__module__', '__main__'),
            ('__qualname__', 'A'),
            ('jedna', &lt;function A.jedna at 0x011027C8>),
            ('dva', &lt;function A.dva at 0x011023D8>),
            ('tři', &lt;function A.tři at 0x011026F0>),
            ('čtyři', &lt;function A.čtyři at 0x01102780>)
        ]) {}

>>> A.members
('__module__', '__qualname__', 'jedna', 'dva', 'tři', 'čtyři')
    </example>
    <!--
    When the class definition for A gets executed, the process begins with calling the metaclass’s __prepare__() method which returns an empty collections.OrderedDict. That mapping records the methods and attributes of A as they are defined within the body of the class statement. Once those definitions are executed, the ordered dictionary is fully populated and the metaclass’s __new__() method gets invoked. That method builds the new type and it saves the ordered dictionary keys in an attribute called members.
    -->

</slide>
<slide title="Poznámky">

    <p>
        Z <em>__new__()</em> můžete popravdě vrátit cokoliv. Třebas i již existující instanci (pokud možno správného typu samozřejmě) místo čerstvě vyrobené nové.
    </p>
    <p>
        Ale možná ještě vtipnější je, že pro napsání metatřídy nakonec nemusíte <em>__new__()</em> vůbec použít. Úplně stačí, pokud <strong>volatelný objekt použitý jako metatřída vrací příslušný nový typ</strong>. A volat se dá v Python'u všechno, co má nadefinováno metodu <em>__call__()</em>. Což jsou kromě tříd mimo jiné třeba i funkce ^_~
    </p>

</slide>
<slide title="Když metatřídy nejsou třídy">

    <p>
        Začněmež rovnou od „obyčejných“ funkcí:
    </p>
    <example lang="python">
>>> def fn(*args, **kwargs):
...     return type('X', (object,), dict(a=1))

>>> class F(metaclass=fn):
...     pass

# třída
>>> F
 &lt;class '__main__.X'>
>>> F.__class__
 &lt;class 'type'>
>>> F.a
 1

# instance
>>> F()
 &lt;__main__.X object at 0x00FDEA70>
>>> F().__class__
 &lt;class '__main__.X'>
>>> F().a
 1
    </example>
    <p>
        To bylo nakonec celkem jednoduché. (Byť kvůli dynamické tvorbě třídy v kontextu funkce, který je po návratu z ní zapomenut, můžeme mít kapku problém s introspekcí.)
    </p>

</slide>
<slide title="Metoda „__call__()“ &amp; singletony I">

    <p>
        S třídami to ale může být horší. Když totiž šikovně přepíšete jejich metodu <em>__call__()</em>, nemusíte se v odůvodněných případech s <em>__new__()</em> vůbec babrat. Jako třeba v následujícím způsobu přípravy <em>singletonu</em>:
    </p>
    <example lang="python">
>>> class Singleton(type):
...     def __init__(self, *args, **kwargs):
...         print('__init__:', self, args, kwargs)
...         super().__init__(*args, **kwargs)
...         self.__instance = None
...     def __call__(self, *args, **kwargs):
...         print('__call__:', self, args, kwargs, self.__instance)
...         if self.__instance is None:
...             self.__instance = super().__call__(*args, **kwargs)
...         return self.__instance

>>> class Třída(metaclass=Singleton):
...     def __init__(self, *args, **kwargs):
...         print('Třída.__init__:', self, args, kwargs)
...         self.args = args
...         self.kwargs = kwargs
__init__: &lt;class '__main__.Třída'>
          ('Třída', (), {
              '__qualname__': 'Třída',
              '__module__': '__main__',
              '__init__': &lt;function Třída.__init__ at 0x0113E108>
          })
          {}

# 1. volání: dojde k nastavení atributů
>>> t = Třída(1, a=2)
__call__: &lt;class '__main__.Třída'>
          (1,)
          {'a': 2}
          None
Třída.__init__: &lt;__main__.Třída object at 0x011381F0> (1,) {'a': 2}
>>> t.args, t.kwargs
 ((1,), {'a': 2})

# 2. volání: atributy se nepřepíší, protože jsou již jednou nastavené
>>> t = Třída(3, a=4)
__call__: &lt;class '__main__.Třída'>
          (3,)
          {'a': 4}
          &lt;__main__.Třída object at 0x011381F0>
>>> t.args, t.kwargs
 ((1,), {'a': 2})
    </example>
    <note>
        Volně podle <a href="http://stackoverflow.com/questions/674304/pythons-use-of-new-and-init" class="external">Stack Overflow</a>.
    </note>
    <p>
        Jelikož zavolání instance vyvolá poděděnou metodu <em>__call__()</em> z příslušné metatřídy, která se aplikuje na volající instanci, dojde tím ke kontrole atributu <em>__instance</em> a podle jejího výsledku bude vytvoření pozměněné instance buď dovoleno nebo zakázáno.
    </p>

</slide>
<slide title="Metoda „__call__()“ &amp; singletony II">

    <p class="enumerate">
        V předchozím příkladu můžete stejně dobře použít i variantu bez <em>super()</em>..
    </p>
    <example lang="python">
        class Singleton(type):
            def __init__(self, *args, **kwargs):
                self.__instance = None
            def __call__(self, *args, **kwargs):
                if self.__instance is None:
                    self.__instance = super().__call__(*args, **kwargs)
                return self.__instance
    </example>
    <p>
        ..nebo dokonce úplně bez <em>__init__()</em>:
    </p>
    <example lang="python">
        class Singleton(type):
            __instance = None
            def __call__(self, *args, **kwargs):
                if self.__instance is None:
                    self.__instance = super().__call__(*args, **kwargs)
                return self.__instance
    </example>
    <p>
        Jen prostě instance musí mít dostupný atribut <em>__instance</em>. Jinak záleží, co konkrétně potřebujete v kterém kroku případně udělat (třeba toho chcete v rámci inicializace třídy „napáchat“ víc, pak ten <em>__init__()</em> využijete).
    </p>

    <p class="enumerate">
        Na úrovni konkrétního objektu můžete samořejmě použít i <a href="introspection.xml?slajd=12">variantu pomocí <em>__dict__</em></a>, ale tahle „metatřídní“ vám vyrobí <em>singletony</em> zadarmo všude pouhým odkazem na příslušnou metatřídu.
    </p>

</slide>
<slide title="Použití">

    <!--
        Class definitions create a class name, a class dictionary, and a list of base classes. The metaclass is responsible for taking those three arguments and creating the class. Most object oriented programming languages provide a default implementation. What makes Python special is that it is possible to create custom metaclasses.
        They have been used for logging attribute access, adding thread-safety, tracking object creation, implementing singletons, and many other tasks.
        The potential uses for metaclasses are boundless. Some ideas that have been explored include logging, interface checking, automatic delegation, automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.
    -->
    <p>
        Jelikož sám OOP prakticky vůbec nepoužívám, návrhy na použití metatříd naprosto bez invence opisuji z dokumentace:
    </p>
    <ul>
        <li>
            logování;
        </li>
        <li>
            kontrola interfejsů;
        </li>
        <li>
            automatická delegace;
        </li>
        <li>
            automatická výroba atributů;
        </li>
        <li>
            proxy;
        </li>
        <li>
            frejmworky;
        </li>
        <li>
            automatické uzamykání zdrojů a synchronizace;
        </li>
        <li>
            …
        </li>
    </ul>

</slide>
  

</lecture>
