<?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>Anotace proměnných</title>
  <date>2017-06-29</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
-->


<slide title="Úvod">

  <p>
    Python je dynamicky typovaný jazyk, což znamená, že o žádném místě v paměti (referencovaném nějakým jménem z programu) se dopředu neví, co v něm je, dokud se tam interpretr nepodívá. Což je sice pohodlné pro rychlou práci, nicméně to nebývá – alespoň v případě hlavních interpretrů Python'u – příliš rychlé.
  </p>
  <p>
    Nejen z tohoto důvodu si tak postupem času spousta lidí začala do pythoních programů dopisovat označení typů a k nim armádu nástrojů na jejich ověřování či rovnou vynucování. Zkrátka typy..
  </p>
  <ul>
    <li>se někomu hodí;</li>
    <li>jsou některým projektům užitečné;</li>
    <li>usnadňují práci jistým nástrojům;</li>
    <li>…</li>
  </ul>
  <p>
    A tak časem vývojáři Python'u vzali uvedený stav na vědomí a ustavili pro Python 3.0+ způsob zápisu tzv. <a href="/materialy/python/functions/annotations.xml"><em>anotací funkcí</em></a> (<em>function annotations</em>) a později i anotace všech ostatních objektů, tedy proměnných globálních i třídních. O kterých právě je tato přednáška.
  </p>

</slide>
<slide title="Zápis">

  <p>
    Anotace proměnných se zapisují takto:
  </p>
  <example lang="python">
    # proměnná bez výchozí hodnoty
    PROMĚNNÁ: VÝRAZ

    # proměnná s výchozí hodnotou
    PROMĚNNÁ: VÝRAZ = výchozí_hodnota
  </example>
  <p>
    Pár příkladů:
  </p>
  <example lang="python">
    kapitán: str

    prvočísla: list[int] = []
  </example>

</slide>
<!--slide title="Poznámka">

  <p>
    Popravdě už <a href="https://www.python.org/dev/peps/pep-0484/" class="external">PEP 484</a> o anotacích funkcí zavedl i anotace proměnných, ovšem v poněkud nešikovné – byť pochopitelné – podobě:
  </p>
  <example lang="python">
    PROMĚNNÁ = výchozí_hodnota  # type: VÝRAZ
  </example>
  <p>
    Nepřekvapivě je doporučeno používat novější způsob zápisu.
  </p>

</slide-->
<slide title="Přístup k anotacím">

  <p>
    Anotace proměnných zavedených na úrovni modulu jsou přítomné v magickém atributu <strong><em>__annotations__</em></strong> příslušného modulu:
  </p>
  <example layout="vertical">
    <program src="_files/anotace.py" lang="python"/>
    <out src="_files/anotace.out" lang="text"/>
  </example>
  <p>
    Tedy <em>__annotations__</em> je slovník, jehož klíči jsou názvy příslušných proměnných a hodnotami odpovídající anotace.
  </p>

</slide>
<slide title="Jaké typy používat?">

  <p class="enumerate">
    Anotací může být jakýkoliv výraz:
  </p>
  <example lang="python">
    >>> seznam: 'můj seznam čísel' = []
    >>> __annotations__
    {'seznam': 'můj seznam čísel'}
  </example>
  <p>
    Jelikož se celkem pochopitelně předpokládá, že anotovat se bude především (základními) typy, byla – po přechodném období, kdy pro složené typy uvedenou funkcionalitu zajišťoval modul <code>typing</code> – od verze Python'u 3.9 upravena funkcionalita základních typů tak, aby je bylo možno použít ve všech těchto situacích:
  </p>
  <example lang="python">
    prvočísla: list[int] = []
  </example>
  <note>
    Než se základní konstruktory „naučily“ rozumět výrazům s <code>[]</code>, musela se stejná funkcionalita zapsat takto:
    <example lang="python">
      from typing import List

      prvočísla: List[int] = []
    </example>
  </note>

  <p class="enumerate">
    Kromě toho je vřele doporučováno zavádět si vlastní výmluvné aliasy na „běžné“ typy, například:
  </p>
  <example lang="python">
    Url = str

    adresa: Url = 'http://vyuka.ookami.cz'
  </example>

</slide>
<slide title="Anotace třídních proměnných">

  <p>
    Samozřejmě je možno anotovat i proměnné tříd – anotace jsou pak (nepřekvapivě) dostupné na magickém atributu <em>__annotations__</em> příslušné třídy (obšlehnuto z dokumentace):
  </p>
  <example lang="python">
    >>> from typing import ClassVar

    >>> class BasicStarship:
    ...     captain: str = 'Picard'
    ...     damage: int
    ...     stats: ClassVar[dict[str, int]] = {}

    >>> BasicStarship.__annotations__
    {'captain': &lt;class 'str'>,
     'damage': &lt;class 'int'>,
     'stats': typing.ClassVar[dict[str, int]]}
  </example>
  <note>
    A právě anotace třídních proměnných (tedy odlišení od proměnných instančních; zde pomocí speciální třídy <em>typing.ClassVar</em>) je jeden z důvodů, proč i dnes zůstává v Python'u modul <code>typing</code>, přestože od verze 3.9 již není potřeba pro anotace složených typů – zatím není k dispozici lepší způsob, jak příslušné proměnné označit jako třídní. Na druhou stranu má <code>PEP 526</code> status <em>„provisional“</em>, protože se způsob anotací pro třídní proměnné (a případně i jiné věci kolem anotací) nikomu moc nezamlouvá.
  </note>

</slide>
<slide title="Poznámka">

  <p>
    Je třeba nezapomínat na to, že Python byl/je/zůstává dynamicky typovaným jazykem a žádný z jeho interpretrů nemá v popisu práce s anotacemi dělat cokoliv jiného, než je „cpát“ do atributu <em>__annotations__</em>.
  </p>
  <p>
    Anotace v Python'u jsou a dlouho (navěky?) budou pouze pro potřeby externích nástrojů (či editorů), které nemusí vůbec provádět typovou kontrolu, ale klidně něco úplně jiného (například budovat lepší nápovědu).
  </p>

</slide>
<slide title="PS: Reference na budoucí objekty">

  <p>
    Nasazení anotací v praxi ukázalo některé slabiny původní implementace. Konkrétně nástroje napovídající typy se nemohly odkazovat na ještě neexistující objekty, protože anotace byly načítány do svých datových struktur v pořadí zpracování zdrojového textu a příslušné objekty tím pádem ještě neexistovaly.
  </p>
  <note>
    A navíc díky anotacím programy startovaly o něco pomaleji.
  </note>
  <p>
    Proto Python 3.7 přinesl <strong>zpožděné vyhodnocování anotací</strong> (<em>postponed evaluation of annotations</em>). Nicméně jelikož je to vlastnost zpětně nekompatibilní, musíte si ji vyžádat v každém modulu importem z <em>__future__</em>:
  </p>
  <example lang="python">
    from __future__ import annotations
  </example>
  <p>
    Uvedené chování bez tohoto importu bude výchozí až v Python'u 4.0.
  </p>

</slide>


</lecture>
