<?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>Generické funkce</title>
  <date>2022-11-26</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>
        Dokonce ještě před rozšířením používání anotací pro označení typů mohlo mít (a mělo) smysl upravit chování funkce podle typu přijatých parametrů.
    </p>
    <p>
        Zatímco klasickým způsobem bylo testování typu při vstupu do funkce (případně dokonce natvrdo <a href="/materialy/python/exceptions/exceptions.xml?slajd=15"><em>aserce</em></a> na typ požadovaný funkcí), jdou na to <strong>generické funkce</strong> jinak:
    </p>
    <blockquote>
        Implementace <strong>generické funkce</strong> je složena z vícero samostatných funkcí, přičemž při zavolání je jedna konkrétní z nich vybrána na základě tzv. <em>výběrového algoritmu</em> (<em>dispatch algorithm</em>).
    </blockquote>
    <p>
        <em>Základní funkce</em>, která definuje jméno celé této „rodiny“, je pak použita v případě, že <em>výběrový algoritmus</em> nedokáže vybrat žádnou ze specifičtějších implementací.
    </p>

</slide>
<slide title="„Single dispatch“ funkce">

    <p>
        S <em>generickými funkcemi</em> se můžeme v Python'u setkat od verze 3.4 ve formě tzv. <strong>„single-dispatch“ funkcí</strong>, neboli funkcí, kde je konkrétní <strong>implementace vybrána na základě typu jediného parametru</strong> (a to toho prvního).
    </p>
    <p>
        Prakticky se tyto funkce zavádějí odekorováním <em>základní funkce rodiny</em> dekorátorem <code>@singledispatch</code> z modulu <code>functools</code>..
    </p>
    <example lang="python">
        from functools import singledispatch

        @singledispatch
        def funkce(argument):
            "Základní funkce generické rodiny spolupracujících funkcí."
            ...
    </example>
    <p>
        ..čímž tato funkce získá funkční atribut <code>register</code>, který funguje jako dekorátor pro související implementace, které <em>přetěžují</em> tuto základní.
    </p>

</slide>
<slide title="Příklad">

    <p>
        Generická funkce s třemi módy chování:
    </p>
    <example layout="vertical">
        <program src="_files/generic.1.py" lang="python"/>
        <out src="_files/generic.1.out" lang="text"/>
    </example>
    <note>
        PS: Ačkoli anotace přes sjednocení typů (zde tedy <code>int | float</code>) funguje už od Python'u 3.10, její použití v generických funkcích až od verze Python'u 3.11!
    </note>

</slide>
<slide title="Poznámky k atributu „register“">

    <p>
        Pár poznámek přímo podle <a href="https://docs.python.org/3/library/functools.html?highlight=singledispatch#functools.singledispatch" class="external">dokumentace</a>:
    </p>

    <p class="enumerate">
        Atribut základu generické funkce <code>register</code> je opravdu normální dekorátor, takže ho můžete použít pro registraci již existující funkce funkcionálním způsobem:
    </p>
    <example lang="python">
        funkce.register(type(None), jiná_funkce)
    </example>

    <p class="enumerate">
        Navíc atribut <code>register</code> při použití vrací originální neodekorovanou funkci (sic!), takže ho můžete vrstvit samého na sebe, přičemž každé použití je nezávislé na ostatních:
    </p>
    <example layout="vertical">
        <program src="_files/generic.2.py" lang="python"/>
        <out src="_files/generic.2.out" lang="text"/>
    </example>

</slide>
<slide title="Přehled implementací – „registry“">

    <p>
        K vlastním implementacím příslušné generické funkce se dá nepřekvapivě dostat i přímo z programu. Schovávají se v atributu <code>registry</code> v podobě mapování („slovníku“) dostupných implementací dané generické funkce:
    </p>
    <example layout="vertical">
        <program src="_files/generic.3.py" lang="python"/>
        <out src="_files/generic.3.out" lang="text"/>
    </example>
    <p>
        PS: <code>&lt;class 'object'&gt;</code> odpovídá základní funkci (tedy implementaci, která bude použita, pokud nebude možno vybrat konkrétnější).
    </p>

</slide>
<slide title="Přehled implementací – „dispatch()“">

    <p>
        Která implementace bude vybrána pro daný vstup, pak umožňuje ukázat metoda <code>dispatch()</code>:
    </p>
    <example layout="vertical">
        <program src="_files/generic.4.py" lang="python"/>
        <out src="_files/generic.4.out" lang="text"/>
    </example>
    <p>
    </p>

</slide>
<slide title="Generické metody">
    
    <p>
        Jelikož metody instancí jsou samozřejmě také funkce, není divu, že Python umožňuje i je psát jako generické. Jen dekorátor k tomu použitý se jinak jmenuje (<code>singledispatchmethod</code>) a z důvodu zachování funkcionality musí býti tím nejposlednějším (tedy tím nejvíce „vnějším“) ze všech. Bez invence příklad přímo z <a href="https://docs.python.org/3/library/functools.html?highlight=singledispatch#functools.singledispatchmethod" class="external">dokumentace</a>:
    </p>
    <example lang="python">
        from functools import singledispatchmethod

        class Negator:
            @singledispatchmethod
            def neg(self, arg):
                raise NotImplementedError("Cannot negate a")

            @neg.register
            def _(self, arg: int):
                return -arg

            @neg.register
            def _(self, arg: bool):
                return not arg
    </example>
    <notes>
        <note>
            Řetězení dekorátorů tedy musí být takovéto:
            <example lang="python">
                @neg.register
                @classmethod
                def _(cls, arg: int):
                    return -arg
            </example>
        </note>
        <note>
            Výběr implementace probíhá podle prvního argumentu za <em>self</em> či <em>cls</em>, jsou-li na metodě přítomny.
        </note>
    </notes>
    
</slide>


</lecture>
