<?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>Serializace – „shelve“</title>
  <date>2012-xx-xx</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>
        Module <strong>shelve</strong> je zajímavou kombinací slovníku, schopností modulu <a href="pickle.xml">pickle</a> a databázového <em>backendu</em> – umožňuje totiž komunikovat s daty zapiklenými na souborový systém (v podobě jisté databáze) pomocí slovníkových metod.
    </p>
    <p>
        Stručně se dá říct, že po otevření příslušné databáze (zcela průhledně pomocí metody <em>open()</em>) se výsledný objekt chová skoro úplně stejně, jako kdyby to byl slovník – k datům se přistupuje pomocí jejich <em>klíče</em> (kterými však mohou být <strong>pouze řetězce</strong>), jen příslušnými <em>hodnotami</em> jsou zapiklené pythoní struktury a celý tento „slovník“ je uložen na souborovém systému.
    </p>
    <note>
        Toto „vzdálené“ uložení dat má ale své drobné, leč důležité mouchy, jak <a href="?slajd=8">uvidíme dále</a>.
    </note>

</slide>
<slide title="Databázový backend">

    <p>
        <em>shelve</em> používá jako databázový backend ten z modulů <code>dbm</code>, který je dostupný. Bohužel však jak <code>dbm.gnu</code>, tak <code>dbm.ndbm</code> fungují pouze na unixových platformách a vyžadují externí software (natožpak kompletně externí modul <em>pybsddb</em>). Je proto dosti pravděpodobné, že nakonec bude <em>shelve</em> používat modul <code>dbm.dumb</code>.
    </p>
    <p>
        Jak už název napovídá, modul <em>dbm.dumb</em> není určen pro rychlé zpracování (je celý napsán v Python'u) a ani není tak používán (a tím pádem ani testován za většího provozu) jako ostatní moduly. Narozdíl od nich se však můžete spolehnout na jeho přítomnost v základní instalaci Python'u.
    </p>
    <note>
        Modul <em>dbm.dumb</em> si při vytvoření databáze založí soubory <code>*.dat</code> a <code>*.dir</code>. Při jejím používání pak přibude soubor <code>*.bak</code>. Jméno souborů odpovídá jménu <em>filename</em>, které přiřadíte databázi otevřením pomocí příkazu <code>shelve.open(filename)</code>
    </note>

</slide>
<slide title="Práce s objektem „shelve“">

    <p>
        Objekt <code>shelve</code> se vlastně chová jako soubor (<em>stream</em>) – před prací s daty ve „slovníku“ je třeba příslušnou databázi otevřít a na konci zase spořádaně uzavřít (jinak riskujete nezapsání dat). Stejně jako u souboru je preferovaným způsobem práce v Python'u 3.x využití kontextu <em>with</em> (za pomoci <code>contextlib.closing()</code>):
    </p>
    <examples>
        <example src="_files/shelf.0a.out" lang="python" />
        <example src="_files/shelf.0b.out" lang="python" />
    </examples>
    <note>
        
    </note>

</slide>
<slide title="Objekt „shelve“ jako slovník">

    <p>
        Objekt <code>shelve</code> podporuje skoro všechny metody slovníku, ale zároveň má několik vlastních:
    </p>
    <example src="_files/shelf.1.out" lang="python" />

</slide>
<slide title="Metody objektu „shelve“">

    <table class="center" border="1" _cellspacing="0">
        <tr>
            <th><code>shelve</code></th>
            <th><code>dict</code></th>
            <th>význam</th>
        </tr>
        <tr>
            <td><em>cache</em></td>
            <td>—</td>
            <td>
                pracovní <em>cache</em> perzistentního slovníku<br/>
                (z praktického hlediska je to objekt <code>dict</code>)
            </td>
        </tr>
        <tr>
            <td><em>clear</em></td>
            <td><a href="/materialy/python/types/dictionaries.xml?slajd=11&amp;par=4">clear</a></td>
            <td></td>
        </tr>
        <tr>
            <td><em>close</em></td>
            <td>—</td>
            <td>sesynchronizuje a uzavře perzistentní slovník</td>
        </tr>
        <tr>
            <td>—</td>
            <td><em>copy</em></td>
            <td></td>
        </tr>
        <tr>
            <td><em>dict</em></td>
            <td>—</td>
            <td>
                objekt starající se o práci s piklenými hodnotami v perzistentním slovníku nad příslušným databázovým <em>backendem</em> <sup>*</sup>
            </td>
        </tr>
        <tr>
            <td>—</td>
            <td><em>fromkeys</em></td>
            <td></td>
        </tr>
        <tr>
            <td><em>get</em></td>
            <td><a href="/materialy/python/types/dictionaries.xml?slajd=9&amp;par=2">get</a></td>
            <td></td>
        </tr>
        <tr>
            <td><em>items</em></td>
            <td><a href="/materialy/python/types/dictionaries.xml?slajd=8&amp;par=1">items</a></td>
            <td></td>
        </tr>
        <tr>
            <td><em>keyencoding</em></td>
            <td>—</td>
            <td>
                deskriptor hodnoty parametru <em>keyencoding</em><br/>
                (určuje kódování, v němž jsou uloženy klíče perzistentního slovníku)
            </td>
        </tr>
        <tr>
            <td><em>keys</em></td>
            <td><a href="/materialy/python/types/dictionaries.xml?slajd=6&amp;par=1">keys</a></td>
            <td></td>
        </tr>
        <tr>
            <td><em>open</em></td>
            <td>—</td>
            <td>otevře perzistentní slovník</td>
        </tr>
        <tr>
            <td><em>pop</em></td>
            <td><a href="/materialy/python/types/dictionaries.xml?slajd=11&amp;par=2">pop</a></td>
            <td></td>
        </tr>
        <tr>
            <td><em>popitem</em></td>
            <td><a href="/materialy/python/types/dictionaries.xml?slajd=11&amp;par=3">popitem</a></td>
            <td></td>
        </tr>
        <tr>
            <td><em>setdefault</em></td>
            <td><a href="/materialy/python/types/dictionaries.xml?slajd=9&amp;par=3">setdefault</a></td>
            <td></td>
        </tr>
        <tr>
            <td><em>sync</em></td>
            <td>—</td>
            <td>
                – vyprázdní <em>cache</em> a provede synchronizaci
                <br/>
                – při <em>writeback=True</em> zapíše všechny čekající (nepřiřazovací) operace
            </td>
        </tr>
        <tr>
            <td><em>update</em></td>
            <td><a href="/materialy/python/types/dictionaries.xml?slajd=10&amp;par=3">update</a></td>
            <td></td>
        </tr>
        <tr>
            <td><em>values</em></td>
            <td><a href="/materialy/python/types/dictionaries.xml?slajd=7&amp;par=1">values</a></td>
            <td></td>
        </tr>
        <tr>
            <td><em>writeback</em></td>
            <td>—</td>
            <td>
                deskriptor hodnoty parametru <em>writeback</em><br/>
                (určuje chování perzistentního slovníku vůči operacím upravujícím jeho obsah)
            </td>
        </tr>
    </table>
    <note>
        <sup>*</sup> Pro <code>dbm.dumb</code> jde o objekt <code>_Database</code>, který je nadstavbou nad <em>ABC</em> (= <em>Abstract Base Class</em>) <code>collections.MutableMapping</code>, která je základem mapovacích typů.
    </note>

</slide>
<slide title="Ukázka">

    <example src="_files/shelf.2.out" lang="python" />

</slide>
<slide title="Přístup ke slovníkové databázi">

    <p>
        Plná forma otevření slovníkové databáze a význam jednotlivých parametrů jest:
    </p>
    <example lang="python">
        shelve.open(filename, flag='c', protocol=None, writeback=False)
    </example>
    <ul>
        <li>
            <code>filename</code> – jméno databázového souboru (či souborů)
        </li>
        <li>
            <code>flag</code> – způsob otevření databáze (<em>r</em> pro čtení, <em>w</em> pro zápis, <em>c</em> pro čtení a zápis, <em>n</em> pro čtení a zápis nad vždy novou databází)
        </li>
        <li>
            <code>protocol</code> – verze piklícího protokolu (podle verze Python'u, takže pro trojkový 3)
        </li>
        <li>
            <code>writeback</code> – způsob zápisu dat do paměti a databáze (viz <a href="?slajd=8">následující slajd</a>)
        </li>
    </ul>

</slide>
<slide title="Dva typy chování při zápisu">

    <p class="enumerate">
        Ve výchozím nastavení atribute <code>writeback=False</code> jsou hodnoty do databáze zapsány <strong>jen a pouze při přímém přiřazení nebo synchronizaci</strong>, tj. pro kód typu:
    </p>
    <example lang="python">
        # přímé přiřazení
        d[_klíč_] = _hodnota_
        
        # synchronizace vyvolaná
        d.sync()
        
        # synchronizace automatická
        d.close()
    </example>
    <note>
        Uvedený poblém můžete (přímo podle dokumentace) obejít např. takto:
        <example lang="python">
            d['xx'] = [0, 1, 2]
            temp = d['xx']
            temp.append(5)
            d['xx'] = temp
        </example>
    </note>
    
    <p class="enumerate">
        Nastavení atributu <code>writeback=True</code> způsobí, že začne fungovat i kód typu..
    </p>
    <example lang="python">
        d['xx'] = [0, 1, 2]
        d['xx'].append(5)
    </example>
    <p>
        ..ovšem za tu cenu, že všechny uvedené operace budou probíhat pouze v paměti (v rámci <em>cache</em> perzistentního slovníku) a k jejich vlastnímu zápisu do databáze dojde až při nejbližší synchronizaci (ať už vyvolané uměle pomocí <em>d.sync()</em> nebo při samotném uzavření databáze pomocí <em>d.close()</em>).
    </p>

</slide>
<slide title="Poznámky">

  <p class="enumerate">
    Jelikož <em>shelve</em> používá na pozadí modul <em>pickle</em> a piklící formát není nijak zabezpečen proti nebezpečným datům =>
    <h1 class="security">Nikdy nenačítejte cizí data!</h1>
  </p>
  
  <p class="enumerate">
    Nezapomínejte na rozdílné chování modulu <em>shelve</em> při zápisu dat podle hodnoty atributu <em>writeback</em>!
  </p>
  
  <p class="enumerate">
    Modul <em>shelve</em> nepodporuje konkurenční přístup při zápisu => má-li jeden program databázi otevřenou pro zápis, nepřipojujte se k ní žádným dalším! Pro čtení však počet přístupů podobné omezení nemá.
  </p>
  
  <p class="enumerate">
    Díky použitému databázovému <em>backendu</em> sdílí modul <em>shelve</em> i jeho nedostatky – ukládané hodnoty nemohou být příliš veliké a v řídkých případech může dojít ke kolizi klíčů.
  </p>

</slide>


</lecture>
