<?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>Zpracování vstupu</title>
  <date>2011-03-10</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
-->

<!--
fileinput
This module implements a helper class and functions to quickly write a loop over standard input or a list of files. If you just want to read or write one file see open().

The typical use is:

import fileinput
for line in fileinput.input():
    process(line)

This iterates over the lines of all files listed in sys.argv[1:], defaulting to sys.stdin if the list is empty. If a filename is '-', it is also replaced by sys.stdin. To specify an alternative list of filenames, pass it as the first argument to input(). A single file name is also allowed.
-->


<slide title="Úvod">
  
  <p>
    Uživatelský vstup do programu můžeme v zásadě rozdělit na dva druhy:
  </p>
  <ul>
    <li>
        přímý vstup z klávesnice do běžícího programu
    </li>
    <li>
        vstup do programu v podobě parametrů
    </li>
  </ul>
  <p>
    Python nabízí několik různých způsobů, jak zařídit obě možnosti vstupu. V dalším se seznámíme se základními z nich.
  </p>

</slide>
<slide title="„input()“">
  
  <p class="enumerate">
    Nejzákladnějším způsobem, jak z běžícího programu vyvolat interakci s uživatelem, je pomocí metody <code>input()</code>:
  </p>
  <example lang="python">
>>> answer = input()
Ahoj, světe!
>>> answer
'Ahoj, světe!'
  </example>
  <p>
    Uvedený kód čeká na zadání vstupního <em>řetězce</em> ukončeného klávesou <code>↵</code>. Zadaný <em>řetězec</em> (bez odřádkování, to jen potvrdilo vstup) je předán jako návratová hodnota funkce (v našem případě tedy do proměnné <em>answer</em>).
  </p>
  <p>
    Má-li vstupem být třeba celé číslo, je třeba vrácený řetězec na celé číslo převést, tj. provést např. <code>answer = int(answer)</code>.
  </p>
  <note>
    V Python'u 2.x se tímto způsobem chovala globální funkce <code>raw_input()</code>.
  </note>

  <p class="enumerate">
    Metoda <code>input([PROMPT])</code> má jeden velmi užitečný nepovinný řetězcový parametr, tzv. <em>výzvu</em> (<em>prompt</em>):
  </p>
  <example lang="python">
>>> answer = input('Zadej text: ')
Zadej text: Ahoj, světe!
>>> answer
'Ahoj, světe!'
  </example>

  <p>
    PS: Chcete-li chování této uživatelské výzvy vylepšit, můžete před jejím použitím naimportovat modul <code>readline</code> (dostupný pouze na platformě Unix!). Vstupní řádka pak „získá“ mimo jiné historii a větší možnosti editace (některé klávesové zkratky apod.).
  </p>

</slide>
<slide title="Zpracování parametrů skriptu">
  
  <p>
    Co se vstupních parametrů skriptu týká, máme v Python'u k dispozici kromě jednoho zcela základního modulu:
  </p>
  <ol start="0">
    <li>
        <code class="em">sys.argv</code> – přístup k parametrům skriptu na nejnižší úrovni
    </li>
  </ol>
  <p>
    ...také několik „generací“ modulů vyšší úrovně:
  </p>
  <ol>
    <li>
        <code class="em">getopt</code> – nejstarší (a nejklasičtější) nadstavba pro práci s argumenty příkazové řádky
    </li>
    <li>
        <code class="em">optparse</code> – vylepšení předchozího modulu; dostupné od verze Python'u 2.3
    </li>
    <li>
        <code class="em">argparse</code> – poslední generace modulů pro zpracování argumentů příkazové řádky; dostupné od Python'u 3.2
    </li>
  </ol>
  <p>
    Zatímco modul <code>sys.argv</code> je zcela základní a veškerou práci s parsováním vstupních agumentů na potřebné hodnoty si musíte obstarat sami, další moduly poskytují nejrůznější vylepšení, jak téhož dosáhnout snadněji. Z praktických důvodů je modulu <code>argparse</code> věnována <a href="argparse.xml">samostatná přednáška</a>.
  </p>

</slide>
<slide title="„sys.argv“">

  <p>
    Nejjednodušším způsobem, jak předat právě spouštěnému programu (skriptu) nějaké údaje, je využít <em>vstupních parametrů skriptu</em>.
  </p>
  
  <p class="enumerate">
    Bez argumentů:
  </p>
  <example layout="horizontal">
    <cmd>python3 argvs.py</cmd>
    <program src="_files/argvs.py" lang="python"/>
    <out src="_files/argvs.out1" lang="text"/>
  </example>
  
  <p class="enumerate">
    S argumenty:
  </p>
  <example layout="horizontal">
    <cmd>python3 argvs.py arg1 arg2 arg3</cmd>
    <program src="_files/argvs.py" lang="python"/>
    <out src="_files/argvs.out2" lang="text"/>
  </example>
  <p>
    Vidíme, že jméno spouštěného programu je předáno jako první prvek v seznamu argumentů, zatímco vše ostatní je „rozsekáno“ podle mezer a předáno jako další prvky seznamu.
  </p>
  
  <p>
    PS: Nezkoušejte míchat parametry s <em>wildcard</em>-znaky, výsledky by vás nejspíš poněkud překvapily...
  </p>
  <handout> TODO </handout>

</slide>
<slide title="„sys.argv“ – argumenty s mezerami">

  <p class="enumerate">
    Pokud bychom chtěli předat jako jeden parametr text, který obsahuje mezery, muzíme ho buď zuvozovkovat (což funguje všude):
  </p>
  <example layout="horizontal">
    <cmd>python3 argvs.py "arg1a arg1b" arg2</cmd>
    <program src="_files/argvs.py" lang="python"/>
    <out src="_files/argvs.out3" lang="text"/>
  </example>
  
  <p class="enumerate">
    ..nebo mezery odiskejpovat (což nezabere pod Windows, ale pod UNIXem je to standard):
  </p>
  <handout>a navíc se to hůř čte</handout>
  <example layout="horizontal">
    <cmd>python3 argvs.py arg1a\ arg1b arg2</cmd>
    <program src="_files/argvs.py" lang="python"/>
    <out src="_files/argvs.out3" lang="text"/>
  </example>
  <note>
    Pokud budou vaše vstupní parametry obsahovat nějaké „nebezpečné“ znaky, jejichž zadání je třeba ošetřit speciálním způsobem, skončíte v praxi nejspíš s různými spouštěcími skripty podle cílového operačního systému (<em>*.cmd</em> nebo <em>*.bat</em> pro Windows, <em>*.sh</em> pro Linux a možná i Mac OS), protože tyto znaky se liší systém od systému (obzvláště Windows jsou velmi restriktivní).
  </note>

</slide>
<slide title="„sys.argv“ – příklad">

  <p>
    Typické použití v programu bude vypadat asi následovně:
  </p>
  <example lang="python">
import sys

if len( sys.argv ) != 2:
    print( "Usage: {} ARGUMENT".format( sys.argv[0] ) )
    sys.exit()
  </example>
  <notes>
    <note>
      Tedy: Očekáváme-li na vstupu argument(y) a nedostaneme je, vypíšeme uživateli správný způsob volání skriptu a pomocí <code>sys.exit()</code> ukončíme jeho vykonávání, aby mohl být znovu zavolán (a tentokrát už snad správně).
    </note>
    <note>
      Alternativně můžete vyrobit nekonečnou smyčku a v ní čekat na správný vstup, ale to už je spíš pro hodně interaktivní program určený „běžným“ uživatelům.
    </note>
  </notes>
  <p>
    Samozřejmě si musíte pohlídat, že uživatel zadal potřebný počet parametrů a ve správném tvaru. Pokud ne, dejte mu o tom vědět a ukončete provádění skriptu. (Respektive použijte nějaké výchozí hodnoty, je-li to možné, ale pak o tom též nějakým způsobem informujte. Alespoň v README :)
  </p>

</slide>
<slide title="„getopt“">

    <p>
        Smyslem tohoto modulu je víceméně jediné – lidem, kteří jsou zvyklí na céčkovskou funkci <em>getopt()</em>, nabídnout její funkčně podobnou obdobu i v Python'u:
    </p>
    <example layout="vertical">
        <cmd>python getopt.mod.py -c stylopis.css --xhtml test.html</cmd>
        <program src="_files/getopt.mod.py" lang="text"/>
        <out src="_files/getopt.mod.out" lang="text"/>
    </example>
    <note>
        Upraveně podle dokumentace.
    </note>
    
    <p>
        Výstupem funkce je seznam dvojic za všechny přítomné přepínače (<code>:</code> a <code>=</code> označují přepínače, které vyžadují vstup) a seznam argumentů. Veškeré testy na povinné přepínače a argumenty a správné hodnoty zadání, stejně jako podrobnou nápovědu si tak musíte kompletně napsat sami, protože <em>getopt</em> za vás zkontroluje prakticky jediné – zda mezi přepínači není nějaký neznámý.
    </p>
    <note>
        Typický rozhodovací kód vypadá následovně:
        <example lang="python">
                for o, a in opts:
                    if o in ("-h", "--help"):
                        usage()
                        sys.exit()
                    elif o in …:
                        …
        </example>
    </note>

</slide>
<slide title="„optparse“ – příklad">

  <p>
    Ukažme si nejdříve chování kódu vybaveného modulem <em>optparse</em> při různých voláních skriptu z příkazové řádky. Daný skript pouze (přehledně) vypisuje, jaké argumenty na příkazové řádce obdržel:
  </p>
  
  <p class="enumerate">
    Nevíme, jak program vůbec zavolat, tak to zkusíme bez parametrů:
  </p>
  <example layout="vertical">
    <cmd>python optparse.mod.py</cmd>
    <out src="_files/optparse.mod.out1" lang="text"/>
  </example>

  <p class="enumerate">
    Aha, takže přepínače nejsou povinné, ale jméno souboru ano. Tak ho zkusíme přidat (<em>test.html</em>):
  </p>
  <example layout="vertical">
    <cmd>python optparse.mod.py test.html</cmd>
    <out src="_files/optparse.mod.out2" lang="text"/>
  </example>

  <p class="enumerate">
    Vida, přepínače mají nějaké výchozí hodnoty. Nebyla by k nim nějaká nápověda?
  </p>
  <example layout="vertical">
    <cmd>python optparse.mod.py --help</cmd>
    <out src="_files/optparse.mod.out3" lang="text"/>
  </example>
  <note>
    A jak je dobrým zvykem, téhož výsledku dosáhneme zavoláním: <code>python3 optparse.mod.py -h</code>
  </note>

  <p class="enumerate">
    Tak to zkusme všechno najednou:
  </p>
  <example layout="vertical">
    <cmd>python optparse.mod.py -x -c stylopis.css test.html</cmd>
    <out src="_files/optparse.mod.out4" lang="text"/>
  </example>
  <notes>
    <note>
        Přepínačem <code>-c</code> jsme změnili stylopisový soubor (klíč <em>cssfile</em>) z výchozího <em>style.css</em> na <em>stylopis.css</em> a přepínačem <code>-x</code> jsme změnili výchozí hodnotu klíče <em>xhtml_flag</em> z <code>False</code> na <code>True</code>. <em>test.html</em> je jméno souboru (určeného ke zpracování).
    </note>
    <note>
        V „dlouhém“ provedení by volání vypadalo: <code>python.exe optparse.mod.py --xhtml --cssfile=stylopis.css test.html</code> (znak <code>=</code> tam sice není povinný, ale lépe se to s ním čte)
    </note>
  </notes>

  <p class="enumerate">
    Aby se neřeklo, tak ještě obligátní dotaz na verzi programu :)
  </p>
  <example layout="vertical">
    <cmd>python optparse.mod.py --version</cmd>
    <out src="_files/optparse.mod.out5" lang="text"/>
  </example>

</slide>
<slide title="„optparse“ – použití">
  
  <p>
    Příklad na předchozím slajdu je upraven podle ukázky na <a class="external" href="http://www.saltycrane.com/blog/2009/09/python-optparse-example/">http://www.saltycrane.com/blog/2009/09/python-optparse-example/</a> (originální dokumentace k modulu <code>optparse</code> je velmi „výživná“):
  </p>
  <example src="_files/optparse.mod.py" lang="python" />
  <!--note>
    Upraveno podle příkladu z <a class="external" href="http://www.saltycrane.com/blog/2009/09/python-optparse-example/">http://www.saltycrane.com/blog/2009/09/python-optparse-example/</a> .
  </note-->

</slide>
<slide title="„optparse“ – poznámky">
  
  <p>
    Už na první pohled je modul <code>optparse</code> poměrně mocný. Pro podrobnosti se podívejte přímo do dokumentace, tady už jen několik poznámek:
  </p>
  <ul>
    <li>
        Přestože se návratová hodnota <em>options</em> z kódu <code>options, args = parser.parse_args()</code> tváří jako slovník, slovník to není :-( Naštěstí to spraví jedno volání funkce <code>vars()</code>, která převádí svůj argument na slovník (alespoň pokud je to objekt vybavený atributem <em>__dict__</em>):
        <example lang="python">
            options, args = parser.parse_args()
            options_dict = vars(options)
        </example>
        <note>
            Použití <code>vars()</code> není úplně bez problémů, ale pokud pouze „vytáhnete“ hodnoty z parseru, nemělo by se nic zvláštního stát.
        </note>
    </li>
    <li>
        Parametr <em>action</em> má k dispozici následující standardní operace: <code>store</code> (výchozí), <code>store_const</code> (uloží hodnotu parametru <em>const</em>), <code>store_true</code>, <code>store_false</code>, <code>append</code> (přidej nalezenou hodnotu do seznamu), <code>append_const</code>, <code>count</code> (inkrementuje odpovídající počitadlo), <code>callback</code> (vyvolá uvedenou funkci s danými parametry), <code>help</code>.
    </li>
    <li>
        S výhodou můžete použít též parametr <em>type</em> – určuje typ argumentu, který příslušný přepínač očekává. Jeho výchozí hodnotou je nepřekvapivě <em>"string"</em> (uvozovky jsou důležité), dále můžete použít <em>"int"</em>, <em>"float"</em>, <em>"complex"</em> a <em>"choice"</em> (viz další bod). Nestačí-li vám to, můžete <em>optparse</em> rozšířit o další typy.
    </li>
    <li>
        Pokud má mít nějaký přepínač pouze jistou množinu povolených hodnot, použijte kombinaci parametrů <em>type="choice" choices=VÝBĚR</em>:
        <example lang="python">
            parser.add_option('-e', '--env',
                              action='store',
                              dest='environment',
                              type='choice',   # přepínač je výběrem z definované množiny hodnot
                              choices=['production', 'staging', 'testing',],   # možné hodnoty přepínače
                              default='production',
                              help='Environment to run on',)
        </example>
        <note>
            Upraveno opět podle <a class="external" href="http://www.saltycrane.com/blog/2009/09/python-optparse-example/">http://www.saltycrane.com/blog/2009/09/python-optparse-example/</a> .
        </note>
    </li>
    <li>
        Přepínače můžete seskupit do více skupin pomocí <code>optparse.OptionGroup</code>:
        <example lang="python">
            # import potřebných konstruktorů
            from optparse import OptionParser, OptionGroup

            # zavedení vlastního parseru a (vizuálních) skupin přepínačů
            parser = OptionParser(..)
            group1 = OptionGroup(parser, 'NÁZEV', 'POPISEK')
            ..

            # A) ke každé skupině se chováme stejně, jako dříve k samotnému parseru – přidáváme do ní vlastní přepínače
            group1.add_option(..)
            ..
            # B) na konci pak skupinu přepínačů přidáme jako celek do parseru
            parser.add_option_group(group1)
        </example>
    </li>
  </ul>

</slide>


</lecture>