<?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í záludnosti</title>
  <date>2014-10-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
-->

<!--
    TODOs:

>>> float('nan')
0: nan
>>> type(nan)
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    type(nan)
NameError: name 'nan' is not defined
>>> type(float('nan'))
1: <class 'float'>
>>> lst = [4.0, 5.0, 42.0, float('nan'), 1.0, 2.0, 3.0]
>>> sorted(lst)
2: [4.0, 5.0, 42.0, nan, 1.0, 2.0, 3.0]

-->


<slide title="Úvod">

    <p>
        Ač je Python vesměs velice pěkný jazyk a spousta jiných si od něj obzvláště v poslední době dokonce nápady vypůjčuje, stejně se neobejde bez slušné řádky menších či větších pastí (a několika vyložených podrazů k tomu).
    </p>
    <p>
        Některá z těch největších překvapení číhají na nepřipraveného programátora naštěstí pouze ve starší dvojkové řadě jazyka, ale i těch ostatních je slušná řádka a přestože jsou často někde zdokumentovaná, dokážou napoprvé nepříjemně překvapit.
    </p>
    <notes>
        <note>
            Za tuhle přednášku může Daniel Kolář, který mi poslal odkaz na <a href="https://speakerdeck.com/alangpierce/python-puzzlers" class="external">Python Puzzlers</a>, kde jsem chvílemi jenom tiše (a občas i méně tiše :-) zíral, čemu všemu se mi zatím podařilo v Python'u vyhnout ^_^ Ve výsledku jsem dospěl k závěru, že by se vyplatilo známé i méně známé podobné podrazy sepsat na jedno místo, abych spoustě lidí ušetřil hromadu šedin a pár rozbitých čel (od mlácení hlavou do stolu nebo zdi).
        </note>
        <note>
            Další použité zdroje: <a href="http://www.onlamp.com/pub/a/python/2004/02/05/learn_python.html" class="external">When Pythons Attack</a>, <a href="http://stackoverflow.com/questions/530530/python-2-x-gotchas-and-landmines" class="external">Python 2.x gotcha's and landmines</a>, <a href="https://www.udacity.com/wiki/common-python-pitfalls" class="external">Common Python Pitfalls</a>, <a href="http://www.toptal.com/python/top-10-mistakes-that-python-programmers-make" class="external">10 Most Common Python Programming Problems</a>, <a href="http://www.toptal.com/python#hiring-guide" class="external">The Insider's Guide to Python Interviewing</a>…
        </note>
    </notes>

</slide>
<slide title="Automatické skládání řetězců">

    <p>
        Na začátek jednu méně známou třešničku (podraz to rozhodně není, ale překvapení nejspíš ano):
    </p>
    <example lang="python">
        >>> xs = 'ahoj' "světe"
        >>> xs
        'ahojsvěte'
    </example>
    <p>
        Python za sebou napsané řetězce totiž automaticky spojuje do jednoho výchozího.
    </p>
    <p>
        PS: Slavný „trik“ s odiskejpováním konce řádku pomocí zpětného lomítka funguje v Python'u také:
    </p>
    <example layout="horizontal">
        <program src="_files/pitfalls.1.py" lang="python"/>
        <out src="_files/pitfalls.1.out" lang="text"/>
    </example>

</slide>
<slide title="Míchání tabulátorů a mezer">

    <p>
        Když přišel Python 3.x, zcela po právu se chlubil, že <strong>žádné míchání tabulátorů a mezer v jednom zdrojovém kódu se už nadále nebude trpět</strong>.
    </p>
    <p>
        Jaké bylo mé překvapení, když jsem si na čerstvě nainstalovaném systému v editoru opožděně zapnul zobrazování bílých znaků a ke své naprosté hrůze jsem zjistil, že debagovaný kód, který Python 3.7 vesele vykonával, obsahuje různou směsici tabulátorů a dokonce i několik typů odsazení mezerami!!!
    </p>
    <note>
        Stejný kód na notebooku vybaveném stařičkým Pythonem 3.4 spustit nešel…
    </note>
    <p>
        Netřeba asi říkat, že uvedené „vylepšení“ parseru pokládám za velmi nešťastné :-(
    </p>

</slide>
<slide title="Oddělování příkazů středníkem">

    <p>
        Ještě jedna z podobného soudku:
    </p>
    <example lang="python">
        import os; os.getcwd(); print(dir(os))
    </example>
    <p>
        No tak, klid, nebrečte, já vím, že vám říkali, že v Python'u nic takového nejde, to zas přejde…
    </p>
    <p>
        PS: Jo, mimochodem, za dvojtečkou také nemusí být vždycky odřádkování:
    </p>
    <example lang="python">
         for i in range(5): print(i); print("Dobře, teď už brečet můžete…")
    </example>
    <note>
        Ano, skutečně oba <em>print</em>y patří do těla téže smyčky…
    </note>

</slide>
<slide title="„Náhodné“ výchozí kódování textových souborů">

    <p>
        Zkuste zodpovědět otázku, v jakém kódování bude otevřen textový soubor následujícím příkazem:
    </p>
    <example lang="python">
        f = open('soubor.txt')
    </example>
    <p>
        Správná odpověď zní: „Pokud pouštíte skript na tom a tom operačním systému, tak asi nejspíš v tom a tom kódování, ale není to jisté.“
    </p>
    <p>
        Ano, je to bohužel tak – <strong>Python nemá žádné pevně dané výchozí kódování pro práci s textovými soubory</strong><sup>*</sup>. Pokud kódování neuvedete, použije se aktuální výchozí systémové. Takže pokud všechny své soubory nemáte jenom v ASCII, dost dobře vám můžou skripty přestat fungovat hned při přístím apgrejdu operačního systému…
    </p>
    <note>
        <sup>*</sup> Ale bude mít, ve čtyřce, UTF-8. O verzi později, než mít měl… (Mimochodem trojku můžete spustit s parametrem <code>python -X utf8</code>, a pak se tak chová také…)
    </note>
    <p>
        Poučení je jasné – při práci s textovými soubory <strong>zásadně uvádějte kódování</strong>!
    </p>

</slide>
<slide title="Výchozí kódování zdrojových kódů">

    <p class="py2k">
        Bohužel úplně stejná poznámka se týká i zdrojových kódů ve dvojkovém Python'u – když si nějaké kódování nevyberete a neuvedete ho na začátku souboru (na konci ukážu jak), použije se výchozí systémové. Hodně štěstí se sdílením zdrojáků s dalšími lidmi…
    </p>
    <p class="py3k">
        Trojkový Python se bohudík pochlapil, takže v něm když neuvedete žádné kódování, bude trvat na UTF-8 a vaše smůla, když to budete zkoušet psát v jiném.
    </p>
    <p>
        Zde je univerzální řešení podobné jako u textových souborů – prostě si kódování vyberte a napište ho do hlavičky, například takto:
    </p>
    <example lang="python">
        # encoding: utf-8
    </example>
    <p>
        Python zkousne téměř libovolný popis, pokud v něm někde na začátku bude podřetězec <em>coding</em> a na konci identifikace příslušného kódování.
    </p>
    <p>
        PS: Prokažte sobě i ostatním službu a pište všechno radši rovnou pěkně v Unicodu.
    </p>

</slide>
<slide title="Není Unicode jako Unicode">

    <p>
        Na druhou stranu – ačkoli <a href="/materialy/text/encoding.xml">je Unicode geniální myšlenka</a> a algoritmus UTF-8 pro jedno z jeho mnoha kódování jakbysmet, nebylo a není vyhodnocování souborů v tomto konkrétním kódování vždy bez problémů. Ty vesměs pramení z toho, že soubor zakódovaný podle UTF-8 může, ale nemusí mít svůj příslušný <a href="/materialy/text/encoding.xml?slajd=7">BOM <code>EF BB BF</code></a>.
    </p>
    <p>
        Problém je zakukleně přítomen i v Python'u, protože:
    </p>
    <blockquote>
        Načtete-li soubor v kódování <em>UTF-8 Signature</em> (tedy s BOMem) jako <code>utf-8</code>, příslušné tři bajty BOMu ze začátku souboru probublají do řetězcových operací, ačkoli se při tisku budou zásadně tvářit jako prázdný řetězec!
    </blockquote>
    <p>
        Prakticky se to projeví <strong>například tak, že když se pokusíte začátek souboru porovnat s nějakým řetězcem, porovnání selže, přestože <em>print()</em> příslušné části bude tvrdit, že není důvod</strong> (protože se vytiskne přesně to, co byste čekali – totiž očekávaný řetězec, pěkně bez BOMu).
    </p>
    <p>
        Snad z důvodů interoperace s jinými programy Python uvedený problém „vyřešil“ tak, že zavedl ještě jedno kódování <code>utf-8-sig</code>, které při čtení uvedené tři bajty na začátku odstraní<sup>*</sup> a při zápisu je tam naopak přidá. Tak tak, v jednoduchosti je síla… <em>*povzdech*</em>
    </p>
    <note>
        <sup>*</sup> Ale pouze jednou, kdybyste náhodou někdy natrefili na šílený soubor s vícero BOMy na začátku. Zvlášť dřív to asi nebýval až tak takový problém.
    </note>

</slide>
<slide title="„Řetězce“ v Python'u 2.x">

    <p class="py2k">
        Když už jsme u těch řetězců a kódování – <strong>ve dvojkovém Python'u vlastně nic takového jako textové řetězce ani neexistuje…</strong>
    </p>
    <p>
        Dvojkový Python totiž u řetězců, které jsou označeny jenom uvozovkami, používá stejný princip jako u zdrojáků a souborů – interně je pokládá za obyčejné sekvence bajtů (tedy za osmibitové řetězce!) a jen pro vás je vyhodnocuje ve výchozím kódování aktuálního operačního systému. A samozřejmě všechny knihovny, které takovéhle „řetězce“ přebírají na vstupu, je také pokládají za obyčejné sekvence bajtů.
    </p>
    <note>
        Teď už asi začínáte chápat, jaké průšvihy na vás mohou čekat při převodu kódu z dvojkové do trojkové řady, pokud v něm často takovéhle „řetězce“ někam předáváte…
    </note>
    <p class="py3k">
        Ve trojkovém Python'u jsou už naštěstí řetězce skutečné řetězce (a to unicodové). Ve dvojkovém Python'u takové řetězce získáte označením <code>u""</code>.
    </p>
    <note>
        Kteréžto označení díky djangistům, kterým se blbě udržovala verze kódu pro obě řady, od verze 3.4 zase můžete použít. Ale v novém kódu jen pro trojkový python pro to není naprosto žádný důvod a jestli ho u někoho uvidím, tak budu tahat za uši!
    </note>

</slide>
<slide title="Cesty k souborům">

    <p>
        Dejte si také pozor na otevírání souborů v „podivných“ cestách „wokenním“ způsobem se zpětnými lomítky, např.:
    </p>
    <example lang="python">
        open('thedir\newfile.txt')
    </example>
    <p>
        Za prvé je vůbec zbytečné zpětná lomítka používat, protože Python si většinu cest pohlídá na všech platformách správně i s klasickými dopřednými lomítky, a za druhé se tím právě vyhnete problémům ve chvíli, kdy kombinace zpětného lomítka a prvního znaku následující cesty „vyrobí“ zřejmě nezamýšlený řídicí znak (jako právě zde znak nové řádky <code>\n</code>).
    </p>
    <!--
        http://pythonconquerstheuniverse.wordpress.com/2008/06/04/gotcha-%E2%80%94-backslashes-are-escape-characters/
    -->

</slide>
<slide title="Relativní cesty k souborům">

    <p>
        Po každé písemce opravuji absolutní cesty ze studentských počítačů, abych mohl programy vůbec spustit. A to přesto, že neustále zdůrazňuji, že relativní cesty jsou mnohem lepší. Bohužel, není to tak úplně pravda – zkuste si kupříkladu následující kód s otevřením souboru (zde sama sebe) spustit ze dvou různých míst:
    </p>
    <example lang="python" src="_files/pitfalls.2.py" />
    <p>
        Pustíme-li program ze stejného adresáře, ve kterém je umístěný, je vše v pořádku a podle očekávání:
    </p>
    <example layout="vertical">
        <cmd>$ python pitfalls.2.py</cmd>
        <out src="_files/pitfalls.2-a.out" lang="text"/>
    </example>
    <p>
        Když ovšem zkusíte totéž s programem zavolaným v relativní cestě, máte problém:
    </p>
    <example layout="vertical">
        <cmd>$ python _files/pitfalls.2.py</cmd>
        <out src="_files/pitfalls.2-b.out" lang="text"/>
    </example>
    <p>
        A dokonce vám nepomůže ani zavolat program s absolutní cestou! Prostě <strong>jak není interpret spuštěn v místě umístění programu, relativní cesty v programu nebudou fungovat</strong> tak, jak byste – podle mě po právu – čekali. Taky vám to přijde divné? 0_o
    </p>
    <handout>
        Tohle přeci musí být někde zdokumentované? 0_o
    </handout>
    <note>
        PS: A na <a href="/materialy/python/modules/_modules.xml?slajd=8">stejný průšvih</a> narazíte u importu modulů, pokud se pokusíte upravit <code>sys.path</code> za běhu programu spuštěného takto relativně vůči místu zavolání interpretru.
    </note>

</slide>
<slide title="Formátování řetězců">

    <p class="py3k">
        A ještě jednou k řetězcům, tentokráte k jejich formátování trojkovým způsobem:
    </p>
    <example lang="python">
>>> s = 'ahoj'
>>> f'{s}'
'ahoj'
    </example>
    <p>
        A teď si představte, že chcete formátovanou hodnotu vypsat zrovna ve složených závorkách. Tak je zkusme pro začátek zdvojit:
    </p>
    <example lang="python">
        >>> f'{{s}}'
        '{s}'
    </example>
    <p>
        Tedy zrovna tohle jsem opravdu nečekal, to spíš nějakou chybu… Hm, speciální funkce znaků v řetězcích se v Python'u přece ruší pomocí zpětného lomítka, že? Tak to zkusme:
    </p>
    <example lang="python">
        >>> f'\{{s}\}'
          File "&lt;stdin&gt;", line 1
            f'\{{s}\}'
                      ^
        SyntaxError: f-string: single '}' is not allowed
    </example>
    <note>
        Chybové hlášení se může lišit podle verze Python'u nebo při použití funkce <em>format()</em>, nicméně neprojde to nikdy. A kdybyste ho náhodou vzali vážně:
        <example lang="python">
            >>> f'\{{s}}'
            '\\{s}'
        </example>
    </note>
    <p>
        No tak co opačně?
    </p>
    <example lang="python">
        >>> f'{\{s\}}'
          File "&lt;stdin&gt;", line 1
            f'{\{s\}}'
                      ^
        SyntaxError: f-string expression part cannot include a backslash
    </example>
    <p>
        No dobře, takhle to teda zjevně nepůjde. Tak se zkusíme rozšoupnout a dáme tam ty složené závorky třikrát:
    </p>
    <example lang="python">
        >>> f'{{{s}}}'
        '{ahoj}'
    </example>
    <p>
        Bingo! Inu, to víte – v čitelnosti je přeci síla…
    </p>
    <note>
        A při použití funkce <em>format()</em> s automatickým doplňováním parametrů je výsledek obzvláště hyenoidní:
        <example lang="python">
            >>> '{{{}}}'.format(s)
            '{ahoj}'
        </example>
    </note>

</slide>
<slide title="„True + True“">

    <p class="py3k">
        I ve trojkovém Python'u jde pořád udělat následující..
    </p>
    <example lang="python">
        >>> True + True
        2
    </example>
    <p>
        ..protože <em>Boolean</em> je jenom podtyp <em>int</em>. Na druhou stranu už nejdou aspoň pravdivostní identifikátory přepsat:
    </p>
    <example lang="python">
        >>> True = 'ahoj'
          File "&lt;stdin>", line 1
        SyntaxError: can't assign to keyword
    </example>
    
    <p class="py2k">
        Ve dvojkovém Python'u to ovšem ještě klíčová slova nejsou:
    </p>
    <example lang="python">
        >>> True + True
        2
        >>> True = 'ahoj'
        >>> True + True
        'ahojahoj'
    </example>

</slide>
<slide title="Bublání proměnných smyčky ven">

    <p>
        Ve většině spořádaných jazyků se dá proměnná cyklu zavést jako interní pouze pro daný cyklus. Python však cyklem nový kontext neotevírá a navíc všechny další případné proměnné stejně musíte zavést dopředu, takže v ní nepřekvapivě zůstane viset poslední hodnota ze smyčky:
    </p>
    <example lang="python">
        >>> for x in range(5):
        ...   print(x)
        ...
        0
        1
        2
        3
        4
        >>> print(x)
        4
    </example>
    <p>
        PS: Někdy se to řeší explicitním smazáním proměnných cyklu pomocí příkazu <code>del</code> ihned po ukončení smyčky.
    </p>

</slide>
<slide title="Bublání ven proměnných generátorové notace (Python 2.x)">

    <p class="py2k">
        Zatímco předchozí podivnost je možná nešťastnou (byť někdy hodící se) vlastností Python'u, následující vlastnost dvojkové řady je skutečný podraz <i>par excellence</i>:
    </p>
    <example lang="python">
        >>> [x for x in range(5)]
        [0, 1, 2, 3, 4]
        >>> x
        4
    </example>
    <p>
        Co heršoft dělá pomocná proměnná z generátoru seznamu v globálním kontextu?!?
    </p>

    <p class="py3k">
        Opět bohudík ve trojkovém Python'u už na dotaz o hodnotě proměnné <code>x</code> dostanete očekávanou výjimku <code>NameError: name 'x' is not defined</code>. Další z mnoha důvodů, proč používat trojkovou řadu ^_~
    </p>

</slide>
<slide title="Bublání ven pomocných proměnných generátorové notace">
    
    <p>
        Bohužel zavedení operátoru <a href="/materialy/python/overview.0.xml?slajd=46"><code>:=</code></a> (aka <em>walrus</em> čili „mrož“) v Python'u 3.8 do předchozího opět hodilo vidle:
    </p>
    <example lang="python">
>>> [z for x in 'ahoj' if (z := ord(x)) % 2 == 0]
[104, 106]
>>> x
Traceback (most recent call last):
  File "&lt;pyshell#3&gt;", line 1, in &lt;module&gt;
    x
NameError: name 'x' is not defined
>>> z
106
    </example>
    <p>
        Asi nám není souzeno, aby Python někdy dokonvergoval do stavu bez podrazů…
    </p>
    
</slide>
<slide title="Iterace po měněném objektu">

    <p>
        Zatímco v následujícím kódu vypadá pokus o odstranění všech výskytů písmene <em>a</em> v pořádku..
    </p>
    <example lang="python">
        >>> xs = list('abrakadabra')
        >>> for x in xs:
        ...   if x == 'a':
        ...     xs.remove('a')
        ... 
        >>> xs
        ['b', 'r', 'k', 'd', 'b', 'r']
    </example>
    <p>
        ..hned ten další ukáže, že to je jen náhoda:
    </p>
    <example lang="python">
        >>> xs = list('-----ahojte')
        >>> for x in xs:
        ...   if x == '-':
        ...     xs.remove('-')
        ... 
        >>> xs
        ['-', '-', 'a', 'h', 'o', 'j', 't', 'e']
    </example>
    <p>
        Jako mnozí jiní totiž Python připraví iterátor smyčky „dopředu“ a následně už jeho provázání s aktuální realitou nekontroluje.
    </p>
    <note>
        Což nám názorně předvede rozklad smyčky pomocí iterátoru:
        <example lang="python">
            >>> xs = list('abrakadabra')
            >>> it = iter(xs)
            # První znak je spořádaně první „a“:
            >>> next(it)
            'a'
            >>> xs.remove('a')
            # Jenomže jeho odstraněním jsme seznam zkrátili a další krok tak
            #   přeskočí „b“!
            >>> next(it)
            'r'
            >>> next(it)
            'a'
            >>> xs.remove('a')
            # Tady zase přeskočíme „k“:
            >>> next(it)
            'a'
            >>> xs.remove('a')
            # A tady „d“:
            >>> next(it)
            'a'
            >>> xs.remove('a')
            # A ještě i to druhé „b“:
            >>> next(it)
            'r'
            >>> next(it)
            'a'
            >>> xs.remove('a')
            >>> next(it)
            Traceback (most recent call last):
              File "&lt;stdin>", line 1, in &lt;module>
            StopIteration
            >>>
        </example>
    </note>

</slide>
<slide title="„while“ a přiřazení">

    <p>
        Když už jsme u těch iterací – v Python'u nejde napsat:
    </p>
    <example lang="python">
        while PROMĚNNÁ = VÝRAZ:
            …
    </example>
    <p>
        Je to úmyslné rozhodnutí již z počátků Python'u, aby se zamezilo chybám, kdy někdo nechtěl přiřadit ale porovnat. (<strong>Proč tedy hernajs radši nevymysleli jiné přiřazení nebo porovnání?</strong>)
    </p>
    <note>
        Podle kontextu je doporučovaným pythoním řešením buď smyčka <em>for-in</em> (při iteraci nad souborem třeba) nebo použití idiomu <em>while True:</em>
        <example lang="python">
            while True:
                PROMĚNNÁ = VÝRAZ
                if not PROMĚNNÁ:
                    break
                …
        </example>
    </note>
    <p>
        Ale nemusíte smutnit – v létě roku 2018 Guido van Rossum prosadil přes všeobecnou nevoli <a href="https://www.python.org/dev/peps/pep-0572/">PEP 572</a> (možná nepřekvapivě jeho poslední krok v roli BDFL, <a href="https://hub.packtpub.com/why-guido-van-rossum-quit/" class="external">jíž se následně vzdal</a>), který umožňuje použití nového operátoru <code>:=</code> mimo jiné v následujícím kontextu:
    </p>
    <example lang="python">
        while chunk := file.read(8192):
            process(chunk)
    </example>
    <p>
        V které verzi Python'u se ale objeví (jestli vůbec nějaké), toť ovšem otázka.
    </p>
    <note>
        Tak nakonec to byla verze 3.8.
    </note>

</slide>
<slide title="Výchozí parametry proměnného typu u funkcí">

    <p>
        Další zdokumentovaná, někdy se dokonce hodící, ale většinou překážející a přinejmenším na pár prvních setkání pěkně zákeřná vlastnost Python'u:
    </p>
    <example lang="python">
        >>> def fn(x, xs=[]):
        ...   xs.append(x)
        ...   print(xs)
        ...
        >>> fn(1)
        [1]
        >>> fn(2)
        [1, 2]
    </example>
    <p>
        WTF?!? Výchozí hodnoty parametrů jsou totiž vyhodnoceny pouze <strong>při prvním volání funkce bez explicitního uvedení příslušných parametrů</strong> a od té chvíle dál už se s nimi pracuje jako se známými. A pochopitelně je to pravda i u metod tříd, takže paseka na entou…
    </p>
    <notes>
        <note>
            Dokud tedy budete funkci volat s oběma parametry, vše se bude tvářit přesně tak, jak by člověk čekal. Teprve ve chvíli, kdy poprvé zavoláte funkci bez druhého parametru <em>xs</em>, bude – poprvé a naposled – vyhodnocena jeho výchozí hodnota.
        </note>
        <note>
            Týká se to samozřejmě pouze parametrů, které jsou proměnnými typy – ty neproměnné jsou z principu zavedeny pokaždé znovu. Pokud je ovšem nezavedete jako <a href="?slajd=18">na dalším slajdu</a>…
        </note>
    </notes>
    <p>
        PS: Někdy se to může hodit – často volaná funkce si takto může třebas do slovníku (který tím pádem nemusí být globální) hešovat vstupy a spočítané výstupy a pekelně se tím zrychlit. Ale často je to ke vzteku a budete to obcházet pomocí:
    </p>
    <example lang="python">
        def f(x, xs=None):
            if xs is None:
                xs = []
            xs.append(x)
            return xs
    </example>

</slide>
<slide title="Výchozí hodnota parametru funkce referencí">

    <p>
        Překvapením ovšem ještě není konec – stačí zavést výchozí hodnotu parametru odkazem na globální proměnnou neproměnného datového typu a hned je všechno také úplně jinak, než by člověk čekal:
    </p>
    <example lang="python">
        >>> x = 3
        ... 
        ... def fn(y=x):
        ...     print(y)
        
        # Tady ještě dobrý:
        >>> fn(5)
        5
        >>> fn()
        3

        # Ale co je tohle? 0_o
        >>> x = 2
        >>> fn()
        3
        >>> fn(4)
        4
        >>> fn()
        3
    </example>
    <p>
        WTF<sup>2</sup>?!? Jo jo, je to tak – Python totiž hlavičku funkce zpracuje pouze při jejím prvním čtení, a v tu chvíli měla proměnná <em>x</em> hodnotu 3, která jí tak „poněkud“ neintuitivně zůstane i nadále…
    </p>

</slide>
<slide title="Jména lokální funkci">

    <p>
        Něco podobného se ale děje i s lokálními objekty uvnitř těl funkcí. Zatímco následující kód v pohodě projde..
    </p>
    <example lang="python">
        >>> xs = 'Ahoj!'
        >>> def fn():
        ...   print(xs)
        ... 
        >>> fn()
        Ahoj!
    </example>
    <p>
        ..tento kód už beznadějně selže:
    </p>
    <example lang="python">
        >>> xs = 'Ahoj!'
        >>> def fn():
        ...   print(xs)
        ...   xs = "Nazdar!"
        ... 
        >>> fn()
        Traceback (most recent call last):
          File "&lt;stdin>", line 1, in &lt;module>
          File "&lt;stdin>", line 2, in fn
        UnboundLocalError: local variable 'xs' referenced before assignment
    </example>
    <p>
        Proč? Python totiž při prvotním překladu kódu (tedy ve fázi, kdy by se při importu vyráběly soubory <em>*.pyc</em>) v těle funkce zjistí, že do proměnné <code>xs</code> bude přiřazeno (zde hodnota <code>Nazdar!</code>), a tudíž ji bude pokládat za lokální pro danou funkci. Jenže když se pak příslušnou funkci pokusí vykonat, najednou po něm budete chtít, aby vytiskl hodnotu proměnné dříve, než byla zavedena.
    </p>
    <note>
        Naproti tomu v prvním příkladě se do <code>xs</code> ve funkci nic nepřiřazuje a Python proto poslušně hledá v nadřazeném kontextu proměnnou stejného jména (kterou najde, a to s hodnotou <code>Ahoj!</code>).
    </note>
    <p>
        PS: Ještě zákeřnější varianta předchozího, protože vše je utopeno a schováno v rámci jediné řádky:
    </p>
    <example lang="python">
        >>> x = 3
        >>> def fn():
        ...   x += 2
        ... 
        >>> fn()
        Traceback (most recent call last):
          File "&lt;stdin>", line 1, in &lt;module>
          File "&lt;stdin>", line 2, in fn
        UnboundLocalError: local variable 'x' referenced before assignment
    </example>
    <!--
    http://web.archive.org/web/20031002184114/www.amk.ca/python/writing/warts.html
    Python's source-to-bytecode compiler tries to optimize accesses to local variables. It decides that a variable is local if it's ever assigned a value in a function. Without the assignment i = i + 1, Python would assume that i is a global and generate code that accessed the variable as a global at the module level. When the assignment is present, the bytecode compiler generates different code that assumes i is a local variable; local variables are assigned consecutive numbers in an array so they can be retrieved more quickly. The print statement, therefore, gets compiled to look for the local i, which doesn't exist yet, and dies with the NameError exception. (In Python 1.6 and later, this raises a different exception, UnboundLocalError; it's hoped that this makes the problem a bit clearer.)
    -->

</slide>
<slide title="Generované konstrukce uvnitř uzávěrů">

    <p>
        Následuje na první pohled zcela neprůhledná chuťovka:
    </p>
    <example lang="python">
        >>> def násobky():
        ...   return [lambda x : i * x for i in range(5)]
        ... 
        >>> for násobek in násobky():
        ...   print(násobek(2))
        ... 
        8
        8
        8
        8
        8
    </example>
    <p>
        Náprava je jednoduchá (tedy pokud nepotřebujete nutně vrátit právě seznam) – stačí místo generované struktury vrátit přímo příslušný generátor:
    </p>
    <example lang="python">
        >>> def násobky():
        ...   return (lambda x : i * x for i in range(5))
        ... 
        >>> for násobek in násobky():
        ...   print(násobek(2))
        ... 
        0
        2
        4
        6
        8
    </example>
    <p>
        Rozdíl mezi oběma příklady je v tom, že v případě generátoru je příslušná vytvořující funkce volána postupně po svých jednotlivých krocích, zatímco vrácení seznamu ji donutí nejdříve celou proběhnout. A protože Python používá při vyhledávání hodnot proměnných v uzávěrech „zpožděné přiřazení“ (<em>late binding</em>), při kterém jsou hodnoty hledány v okolním kontextu až ve chvíli zavolání funkce (nebo jinak: proměnné v uzávěru jsou vázány přeze jméno, nikoli hodnotu), dostane každý prvek seznamu poslední hodnotu proměnné <code>i</code>, tedy 4. Tak trochu symbolicky:
    </p>
    <example lang="python">
        # a) seznam
        [lambda x: i*x, lambda x: i*x, lambda x: i*x,
         lambda x: i*x, lambda x: i*x, ]
        # b) generátor
        yield (lambda x: i*x)
    </example>
    <!--note>
        Podívejme se tomu na zoubek: U první varianty se seznamem dostáváme následující..
        <example lang="python">
            >>> def násobky():
            ...   return [lambda x : i * x for i in range(5)]
            ... 
            >>> cm = násobky()
            >>> cm[0]
            &lt;function násobky.&lt;locals>.&lt;listcomp>.&lt;lambda> at 0x7f1010e36488>
            >>> cm[0](2)
            8
        </example>
        <example lang="python">
            >>> def násobky():
            ...   return (lambda x : i * x for i in range(5))
            ... 
            >>> cm = násobky()
            >>> f = next(cm)
            >>> f
            &lt;function násobky.&lt;locals>.&lt;genexpr>.&lt;lambda> at 0x7f1010e0b7b8>
            >>> f(2)
            0
        </example>
    </note-->
    
    <p>
        PS: Pokud byste nutně potřebovali vrátit nikoli generátor ale skutečně právě a jenom seznam, dá se to s pomocí výchozích parametrů funkcí také zařídit, ale už to asi není na první pohled tak průhledné:
    </p>
    <example lang="python">
        >>> def násobky():
        ...   return [lambda x, i=i : i * x for i in range(5)]
        >>> for násobek in násobky():
        ...   print(násobek(2))
        0
        2
        4
        6
        8
    </example>
    <p>
        S rozpisem vrácené struktury už je to jasnější:
    </p>
    <example lang="python">
        # c) seznam s „i“ jako výchozím parametrem
        [lambda x, i=0: i*x, lambda x, i=1: i*x, lambda x, i=2: i*x,
         lambda x, i=3: i*x, lambda x, i=4: i*x, ]
    </example>
    
    <p>
        Mimochodem – jaké z toho všeho plyne poučení? <strong>Generátorová notace rulez! ^_~</strong>
    </p>

    <note>
        <p>
            PS: Tohle celé <a href="http://math.andrej.com/2009/04/09/pythons-lambda-is-broken/comment-page-1/#comments" class="external">se dá zjednodušit</a> v odpověď na otázku, co má vrátit druhý <code>print()</code> v následujícím kódu:
        </p>
        <example lang="python">
            i = 1

            def f():
                return i

            print(f())        # 1

            i = 2

            print(f())        # ?
        </example>
        <p>
            Python patří k jazykům, které – asi nepřekvapivě – vrátí <code>2</code>. Kdyby vracel <code>1</code>, kód ze začátku by se choval možná očekávaněji, ale na mnoha jiných místech zase vůbec. <strong>Zlaté <em>funkcionální programovací jazyky</em>, ve kterých tenhle problém ani nemůže vzniknout!</strong>
        </p>
    </note>

</slide>
<slide title="„dict().fromkeys()“ a výchozí hodnota proměnného typu">

    <p>
        Podobné a na první pohled zcela nepochopitelné překvapení nás čeká i na zcela nečekaném místě – u metody <code>fromkeys()</code> slovníku:
    </p>
    <example lang="python">
        >>> xs = {}.fromkeys('abc', [])
        >>> xs
        {'a': [], 'b': [], 'c': []}
        >>> xs['a'].append(1)
        ... xs['b'].append(2)
        ... xs['c'].append(3)
        >>> xs
        {'a': [1, 2, 3], 'b': [1, 2, 3], 'c': [1, 2, 3]}
    </example>
    <!--note>
        A ne – výměna <code>[]</code> za <code>list()</code> dá stejný výsledek.
    </note-->
    <p>
        Metoda <code>fromkeys()</code> se totiž (převážně z historických důvodů) v podstatě chová takto..
    </p>
    <example lang="python">
        prázdný_seznam = []
        xs = {}.fromkeys('', prázdný_seznam)
    </example>
    <note>
        A v dokumentaci je to výslovně uvedené: <em>All of the values refer to just a single instance, so it generally doesn’t make sense for value to be a mutable object.</em>
    </note>
    <p>
        ..takže všechno jsou to reference na stejný proměnný objekt 0_o Napravit se to tudíž musí buď <strong>generátorovou notací</strong> (opět :-)..
    </p>
    <example lang="python">
        >>> xs = {x:[] for x in 'abc'}
        >>> xs
        {'a': [], 'b': [], 'c': []}
        >>> xs['a'].append(1)
        ... xs['b'].append(2)
        ... xs['c'].append(3)
        >>> xs
        {'a': [1], 'b': [2], 'c': [3]}
    </example>
    <p>
        ..nebo ručním zavedením každého prvku slovníku nezávisle na ostatních (pro malý počet klíčů) nebo též použitím <code>defaultdict</code>, má-li to v daném případě smysl.
    </p>
    
</slide>
<slide title="Krajně nebezpečný „input()“ (Python 2.x)">

    <p class="py2k">
        Zkuste uhodnout, co bude v proměnné <code>xs</code>, když následujícímu programu zadáte na vstupu řetězec „os.getcwd()“:
    </p>
    <example lang="python">
        import os
        xs = input('Vstup: ')
    </example>
    <p>
        Ve trojkovém Python'u pěkně spořádaně zadaný řetězec <code>'os.getcwd()'</code>. Ovšem v Python'u dvojkovém to snad radši ani nechcete vědět – <code>'C:\\PROFILES\\pirat'</code>!?! Ano, je to tak – <strong>dvojkový Python automaticky volá na vstup funkce <em>input()</em> extrémně nebezpečnou funkci <em>eval()</em></strong>!!! Radši si ani nezkoušet představit, co všechno se tímhle dá způsobit…
    </p>
    <p>
        Vstup bez vyhodnocení vrací ve dvojkové řadě funkce <code>raw_input()</code> a není se vůbec čemu divit, že v řadě trojkové už je jenom tahle varianta (a s krátkým jménem samozřejmě). Ve dvojkové řadě ve vlastním zájmu snad po uživatelích vstup radši ani vůbec nechtějte.
    </p>

</slide>
<slide title="N-tice že jsou neproměnné?">

    <p>
        Ale kdeže! Stačí si zkusit tohle:
    </p>
    <example lang="python">
        >>> xs = ([1],)
        >>> xs[0] += [2,3]
        Traceback (most recent call last):
          File "&lt;stdin>", line 1, in &lt;module>
        TypeError: 'tuple' object does not support item assignment
        >>> xs
        ([1, 2, 3],)
    </example>
    <p>
        Následující varianta dokonce ani nevyplivne výjimku:
    </p>
    <example lang="python">
        >>> xs = ([1],)
        >>> xs[0].extend([2,3])
        >>> xs
        ([1, 2, 3],)
    </example>
    <br/>
    <note>
        A jak se můžete pomocí funkce <em>id()</em> snadno přesvědčit, v obou případech se i po změně jedná stále o stejný objekt…
    </note>
    <p>
        PS: Do třetice švandy kopec – následující varianta zakřičí výjimku a nic neudělá, to je pane konzistence…
    </p>
    <example lang="python">
        >>> xs = ([1],)
        >>> xs[0] = xs[0] + [2,3]
        Traceback (most recent call last):
          File "&lt;stdin>", line 1, in &lt;module>
        TypeError: 'tuple' object does not support item assignment
        >>> xs
        ([1],)
    </example>
    <p>
        PPS: Částečné zdůvodnění pro toto podivné chování je k nalezení v <a href="https://docs.python.org/3/faq/programming.html#why-does-a-tuple-i-item-raise-an-exception-when-the-addition-works" class="external">Python FAQ</a>.
    </p>

</slide>
<slide title="Jednice">

    <p>
        Když už jsme u těch n-tic. Tohle je vlastně nakonec celkem pochopitelné, ale když to uvidíte poprvé, asi budete chvilku zmateně zírat:
    </p>
    <example lang="python">
        # Tak moment – co to je?
        >>> x, = 'a'
        >>> x
        'a'
        
        # Aha, jednoprvkový iterovatelný objekt:
        >>> x, = ['abc']
        >>> x
        'abc'
    </example>

</slide>
<slide title="„+=“ vs „= +“">

    <p>
        S předchozí podivností <em>proměnných n-tic</em> úzce souvisí rozdíl mezi přiřazením pomocí operátoru <code>+=</code> a jeho nezkrácenou variantou.
    </p>
    <example lang="python">
        # Vytvořme seznam ⇒ obsadí si určité místo v paměti:
        >>> xs = [1,2,3]
        >>> id(xs)
        140322828036936
        
        # Rozšiřme ho pomocí „+=“ ⇒ a je to stále stejný objekt:
        >>> xs += [4]
        >>> xs
        [1, 2, 3, 4]
        >>> id(xs)
        140322828036936
        
        # Rozšiřme ho klasicky ⇒ a vyrobíme nový objekt:
        >>> xs = xs + [5]
        >>> xs
        [1, 2, 3, 4, 5]
        >>> id(xs)
        140322827952904
    </example>
    <p>
        Zatímco klasické <code>= +</code> (implementované jako <em>magická metoda</em> <code>__add__</code>, resp. <code>__radd__</code>) nepřekvapivě nejdříve vyhodnotí pravou stranu výrazu a výsledek přiřadí do <em>nově vytvořeného</em> objektu, byť stejného jména, zkrácené <code>+=</code> (implementované jako <em>magická metoda</em> <code>__iadd__</code>) provádí výměnu v rámci původního objektu (<em>in-place</em>).
    </p>
    <note>
        Dá se očekávat, že změna objektu pomocí <code>+=</code> bude ve většině případů rychlejší – zatímco klasická varianta musí místo pro nový objekt alokovat vždy, zkrácená to bude muset udělat jenom tehdy, když přiřazovaná data přesáhnou místo předalokované proměnnému objektu.
    </note>

</slide>
<slide title="„+=“ vs „= +“ podruhé">

    <p>
        Aby švandě nebyl konec, <code>+=</code> se navíc chová mnohem více jako metoda na příslušném typu (dokáže zkonvertovat svůj vstup na požadovaný tvar) než operátor:
    </p>
    
    <p class="enumerate">
        Především pomocí <code>+=</code> se dá složit téměř cokoliv, byť trochu překvapivě <strong>prvek po prvku</strong>:
    </p>
    <example lang="python">
        >>> xs = [1, 2, 3, 4, 5]
        >>> xs += {6, 7, 8, 9}
        >>> xs
        [1, 2, 3, 4, 5, 8, 9, 6, 7]
    </example>
    <p>
        „Přičítaný“ objekt tedy není přidán jako poslední prvek seznamu, ale je proiterován prvek po prvku a takto také postupně popřidáván.
    </p>
    
    <p class="enumerate">
        Ovšem na druhou stranu výše uvedený trik (pochopitelně) <strong>nefunguje</strong> pro <code>= +</code>:
    </p>
    <example lang="python">
        >>> xs = [1, 2, 3, 4, 5]
        >>> xs = xs + {6, 7, 8, 9}
        Traceback (most recent call last):
          File "&lt;stdin>", line 1, in &lt;module>
        TypeError: can only concatenate list (not "set") to list
    </example>
    <p>
        Operátor prostě implicitní konverze typů neprovádí, zatímco metody objektů (a <code>+=</code> s nimi) to (většinou) umí.
    </p>

</slide>
<slide title="„+=“ vs „= +“ potřetí">

    <p>
        Když budete chtít být skutečně zlí ^_~ , tak někde do kódu napište:
    </p>
    <example lang="python">
        xs =+ x
    </example>
    <p>
        Bude-li proměnná <em>x</em> číslo, můžete nešťastníka luštícího váš kód na chvíli beznadějně zmást, než mu dojde, že uvedený kód se naparsuje jako <code>xs = +x</code>, což pochopitelně neznamená nic jiného než prosté přiřazení <code>xs = x</code>…
    </p>
    <note>
        Uznávám, tohle není pythoní specialita. Ale v pythoním kódu jsem ji viděl poprvé, takže sem patří :-)
    </note>

</slide>
<slide title="Kopie objektů">

    <p>
        Na podobném písečku operuje kopírování stávajících versus vytváření nových objektů. V tomto příkladě kopírujeme stejný objekt třikrát..
    </p>
    <example lang="python">
        >>> xs = [[1,2,3]] * 3
        >>> xs
        [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
        >>> xs[0][1] = 'Baf!'
        >>> xs
        [[1, 'Baf!', 3], [1, 'Baf!', 3], [1, 'Baf!', 3]]
    </example>
    <p>
        ..zatímco v tomto třikrát zavedeme objekt nový:
    </p>
    <example lang="python">
        >>> xs = [[1,2,3] for _ in range(3)]
        >>> xs
        [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
        >>> xs[0][1] = 'Baf!'
        >>> xs
        [[1, 'Baf!', 3], [1, 2, 3], [1, 2, 3]]
    </example>
    <p>
        Někdy se hodí první, ale často budete čekat spíše to druhé. Poučení? <strong>Generátorová notace rulez! Opět ^_^</strong>
    </p>

</slide>
<slide title="Výřezy kašlou na existenci prvků…">

    <p>
        Těžko říct, zda je tohle ošklivý podraz nebo šikovná vlastnost:
    </p>
    <example lang="python">
        >>> xs = [1,2,3,4,5]
        
        # Ptát se na neexistující prvek se nevyplácí:
        >>> xs[4]
        5
        >>> xs[5]
        Traceback (most recent call last):
          File "&lt;stdin>", line 1, in &lt;module>
        IndexError: list index out of range
        
        # Vyrábět neexistující výřez je ale povoleno:
        >>> xs[4:]
        [5]
        >>> xs[5:]
        []
    </example>
    <note>
        PS: Po zpracování několika bioinformatických problémů začínám měnit strany směrem k šikovné vlastnosti ^_~
    </note>

</slide>
<slide title="…a „insert“y také">

    <p>
        Přihlaste se, kdo jste věděl, že <em>insert()</em> na neexistující index nevyhodí <em>IndexError</em>, ale místo toho udělá tohle:
    </p>
    <example lang="python">
        >>> xs = [1, 2, 3, 4, 5]
        >>> xs
        [1, 2, 3, 4, 5]
        >>> len(xs)
        5

        >>> xs.insert(100, 'vpravo')
        >>> xs
        [1, 2, 3, 4, 5, 'vpravo']

        >>> xs.insert(-100, 'vlevo')
        >>> xs
        ['vlevo', 1, 2, 3, 4, 5, 'vpravo']
    </example>
    <p>
        Já tedy ne ^_^ Mimochodem je to testováno v oficiálních testech, takže je to očekávaná vlastnost…
    </p>

</slide>
<!--
<slide title="">

    <p>
    </p>
    <example lang="python">
    </example>

</slide>
-->
<slide title="Import knihoven do relativně volaných skriptů">

    <p>
        Při importu modulů prohledává Python postupně všechny cesty uvedené v seznamu <code>sys.path</code>, počínaje místem spuštění skriptu. Protože je to obyčejný seznam, můžete být v pokušení ho v případě potřeby upravovat z běžícího programu. Většinou to bude fungovat naprosto v pohodě, ale <strong>jakmile se pokusíte spustit pythoní skript relativním voláním z jiného adresáře</strong> (třebas jako <code>python3 cesta/skript.py</code>)<strong>, beznadějně to selže :-(</strong>
    </p>
    <p>
        Zákeřnost tohohle podrazu spočívá především v tom, že by člověk čekal, že fungovat bude, zvlášť když je to napsané v dokumentaci, že…
    </p>
    <p>
        PS: Upravit tento seznam je proto nejlepší nějak systémově – například úpravou proměnné prostředí <code>PYTHONPATH</code> nebo zanesením požadovaných cest do vhodného <em>pth</em>-souboru.
    </p>

</slide>
<slide title="Ach ty třídy… I">

    <p>
        Velká skupina překvapení (a podrazů) je vázána na použití tříd, a to nejlépe ještě mícháním „starých“ a „nových“ tříd ve dvojkovém Python'u.
    </p>
    <p>
        Řekněme si rovnou na začátku, že používat třídy v Python'u má smysl jenom v tom případě, že to skutečně jinak rozumně nejde. (Ostatně velká část samotné <em>standardní pythoní knihovny</em> se bez tříd zcela obejde – často jen pouhé inteligentní rozdělení kódu do modulů a podmodulů vyřeší spoustu případů, kde by se jinak člověk hrnul do použití tříd.) Python není primárně OOP-jazyk, a tak nejen že použití tříd představuje slušnou paměťovou a časovou zátěž (ne že by na tom v běžném Python'u nějak extrémně záleželo), ale bohužel se dokonce dají v pythoních třídách vyrábět takové s odpuštěním prasárny, že by jeden brečel.
    </p>
    <p>
        Ovšem někdy jsou třídy zdaleka nejlegitimnější způsob řešení daného problému, a pak je třeba vědět, jaké překážky vám v nich Python přichystal.
    </p>

</slide>
<slide title="Ach ty třídy… II">

    <p>
        Pravděpodobně asi největším průšvihem (zvláště pro lidi zvyklé na OOP z jiných jazyků, čístě OOP, jazyků) je to, že uvnitř pythoních tříd si můžete volat naprosto cokoliv a odkudkoliv. Třeba i místo <em>init</em>u rodičovské třídy nějaký úplně jiný kód:
    </p>
    <example lang="python">
        TODO
    </example>

</slide>
<slide title="Ach ty třídy… III">

    <p>
    </p>
    <example lang="python">
    </example>

</slide>
<!--
<slide title="">

    <p>
    </p>
    <example lang="python">
    </example>

</slide>
-->


</lecture>
