<?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>Viditelnost proměnných</title>
  <date>2012-04-24</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
-->

<!-- TODO
    - scope promennych (viz priklady environmentX.py) je v Pythonu staticky
-->

<slide title="Viditelnost proměnných">

  <p>
    Z hlediska viditelnosti se proměnné (které v Python'u zastupují libovolné objekty – skutečné „proměnné“, složitější datové struktury, ale třeba i funkce a třídy!) dělí na tři skupiny:
  </p>
  <ul>
    <li>
      <strong>lokální</strong> – jsou „vidět“ pouze v rámci bloku, v němž byly definovány (tj. např. v těle funkce), a ve všech jeho podřízených blocích
    </li>
    <li>
      <strong>globální</strong> – jsou dostupné v celém programu / skriptu / modulu, na jehož nejvyšší úrovni byly zavedeny
    </li>
    <li>
      <strong>nelokální</strong> – umožňují z podbloku odkazovat na proměnné stejného jména v nadřazeném bloku, ale nikoli až na globální úroveň
    </li>
  </ul>
  <note>
    PS: Chcete-li přesto odkázat na globální proměnnou, stačí (ne)lokální přiřadit do nové proměnné, která už odkazuje na globální. Týká se to jak neproměnných (<i>immutable</i>), tak proměnných (<i>mutable</i>) typů.
  </note>

</slide>
<slide title="Proměnné lokální">

  <p>
    Proměnné, funkce a další objekty zavedené (a též moduly importované) uvnitř funkce jsou viditelné právě jen tam – uvnitř funkce:
  </p>
  <example layout="vertical">
    <program src="_files/scope.0.py" lang="python"/>
    <out src="_files/scope.0.out" lang="text"/>
  </example>
  <note>
    To tedy znamená, že si nemusíte hlídat kolize v názvech proměnných – uvnitř funkcí můžete vesele používat stejné názvy, jako mimo ně, a pokaždé budou označovat jiné objekty. Ale pozor – <a href="?slajd=8">platí to pouze při přiřazování</a>!
  </note>

</slide>
<slide title="Proměnné globální">

  <p>
    Pokud byste chtěli skutečně pracovat s proměnnou mimo tělo funkce (tedy zde na globální úrovni), musíte použít klíčové slovo <code>global</code>:
  </p>
  <example layout="vertical">
    <program src="_files/scope.1.py" lang="python"/>
    <out src="_files/scope.1.out" lang="text"/>
  </example>
  <p>
    PS: Dlužno poznamenat, že práce s globálními proměnnými je opodstatněná jen ve výjimečných případech, kdykoliv jindy je pokládána za pěknou <em>—zcenzurováno—</em> ^_~
  </p>

</slide>
<slide title="Proměnné nelokální I">

  <p>
    Pokud uvnitř funkce definujete další funkci (která tím pádem bude <i>lokální</i> vůči nadřazené), budou proměnné uvnitř této funkce opět pouze lokální jen a jen pro ni:
  </p>
  <example layout="vertical">
    <program src="_files/scope.2a.py" lang="python"/>
    <out src="_files/scope.2a.out" lang="text"/>
  </example>

</slide>
<slide title="Proměnné nelokální II">

  <p>
    Pokud se z této vnitřní funkce budete chtít odkazovat na proměnnou v její nadřazené funkci (tedy <em>v nejbližším nadřazeném kontextu</em>, nikoli v globálním kontextu), musíte ji specifikovat pomocí klíčového slova <code>nonlocal</code>:
  </p>
  <example layout="vertical">
    <program src="_files/scope.2b.py" lang="python"/>
    <out src="_files/scope.2b.out" lang="text"/>
  </example>

</slide>
<slide title="Souhrnný přehled">

    <examples type="horizontal3">
        <example layout="vertical">
            <program src="_files/prehled.0.py" lang="python"/>
            <out src="_files/prehled.0.out" lang="text"/>
        </example>
        <example layout="vertical">
            <program src="_files/prehled.1.py" lang="python"/>
            <out src="_files/prehled.1.out" lang="text"/>
        </example>
        <example layout="vertical">
            <program src="_files/prehled.2.py" lang="python"/>
            <out src="_files/prehled.2.out" lang="text"/>
        </example>
    </examples>
    <p>
        Pro chování proměných v kontextu tedy souhrnně platí:
    </p>
    <ul>
        <li>
            <strong>lokální</strong> proměnné si spořádaně „hrabou pouze na svém písečku“, tedy v těle funkce či modulu, kde jsou zavedeny;
        </li>
        <li>
            <strong>globální</strong> proměnné se aplikují pouze na hlavní úrovni celého modulu;
        </li>
        <li>
            <strong>nelokální</strong> proměnné „vidí“ jen a právě o jednu úroveň v hierarchii výše.
        </li>
    </ul>

</slide>
<slide title="Dohledávání jmen I">

    <p class="enumerate">
        Největší „podraz“ se schovává v dohledávání proměnných pro aktuální blok:
    </p>
    <example layout="horizontal">
        <program src="_files/resolving.1.py" lang="python"/>
        <out src="_files/resolving.1.out" lang="text"/>
    </example>
    <p>
        Tento zdánlivě průhledný kus kódu totiž neupadne s výjimkou <em>NameError</em> pro odkaz na neznámou proměnnou, ale pěkně hezky vypíše číslo <code>1</code>. Zřejmě z důvodů optimalizace se totiž proměnné lokální danému bloku vytváří pouze pro přiřazení, při jejich použití (<em>resolving</em>) se prostě hledají i v bloku nadřazeném.
    </p>
    
    <p class="enumerate">
        A to skutečně v nejbližším nadřazeném, jak ukazuje příklad:
    </p>
    <example layout="horizontal">
        <program src="_files/resolving.2.py" lang="python"/>
        <out src="_files/resolving.2.out" lang="text"/>
    </example>

</slide>
<slide title="Dohledávání jmen II">

    <p>
        Běda Vám, pokud byste se však pokusili uvnitř těla funkce <strong>zavést lokální proměnnou stejného jména a přesto se ještě před tím pokoušeli odkázat na jméno globální</strong>:
    </p>
    <example layout="horizontal">
        <program src="_files/resolving.3.py" lang="python"/>
        <out src="_files/resolving.3.out" lang="text"/>
    </example>
    <note>
        Tělo funkce (a samozřejmě i její hlavička) je totiž „připraveno dopředu“, takže v okamžiku jeho vykonávání je již známo rozložení názvů v místní tabulce jmen objektů. Odkaz na jméno <em>x</em> na prvním řádku funkce tak způsobí havárii, protože už půjde o lokální název, ke kterému však zatím není přiřazena žádná hodnota.
    </note>

</slide>
<slide title="Poznámky">

    <p class="enumerate">
        Pomocí klíčového slova <code>global</code> je možné zavést na globální úrovni aktuálního bloku <!--(což tedy znamená třeba i na úrovni modulu)--> <em>jméno proměnné bez přiřazení hodnoty</em> (a tím pádem i typu).
    </p>
    <example lang="python">
        global var
        var = 3
    </example>
    <p>
        Ale použité jsem to viděl snad jen jednou a ještě to s sebou neslo nečekané vedlejší efekty.
    </p>
    
    <p class="enumerate">
        S lokálními proměnnými je to ještě kapku zamotanější (a to i dřív, než se dostaneme k <a href="advanced-1.xml">uzávěrům</a>), jak ukazuje následující příklad:
    </p>
    <example layout="horizontal">
        <program src="_files/lokalni.py" lang="python"/>
        <out src="_files/lokalni.out" lang="text"/>
    </example>
    <p>
        Jakmile se odvoláme na proměnné z nadřazených kontextů (ovšem s výjimkou globálního), objeví se mezi lokálními. Kdo chce vědět víc, než vůbec tušil, že chce vědět, přečte si <a href="https://docs.python.org/3/reference/executionmodel.html#naming-and-binding" class="external">dokumentaci</a> ^_~
    </p>

</slide>


</lecture>
