<?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>Poznámky k datovým typům</title>
  <date>2011-11-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
-->


<!--

5.6. Sequence Types

There are six sequence types: strings, byte sequences (bytes objects), byte arrays (bytearray objects), lists, tuples, and range objects. For other containers see the built in dict and set classes, and the collections module.

Most sequence types support the following operations. The in and not in operations have the same priorities as the comparison operations. The + and * operations have the same priority as the corresponding numeric operations. [3] Additional methods are provided for Mutable Sequence Types.

Sequence types also support comparisons. In particular, tuples and lists are compared lexicographically by comparing corresponding elements. This means that to compare equal, every element must compare equal and the two sequences must be of the same type and have the same length. (For full details see Comparisons in the language reference.)

*******

BEDA
Because a set is hash-based to provide fast lookup, it cannot store mutable types, such as lists or other sets.
This limitation is similar to the limitation imposed on dictionary keys.
To overcome this limitation when using list, we can use tuples which are a immutable counterpart of lists. In case of sets we have a similar type avialable - the frozen set. It is an immutable version of set which can be used both in dictionary keys and in sets. 

-->

<slide title="Základní datové typy – konstruktory">
  
  <table width="80%" align="center" border="1" cellspacing="0" frame="border" rules="groups">
    <col align="center" />
    <col align="center" />
    <col align="left" style="border-left: 1px solid black; font-style: italic;" />
    <thead>
      <tr>
        <th>proměnné (<i>mutable</i>)</th>
        <th>neproměnné (<i>immutable</i>)</th>
        <th>typ</th>
      </tr>
    </thead>
    <tbody>
      <tr align="center">
        <td>—</td>
        <td><code>int()</code></td>
        <td>číselný</td>
      </tr>
      <tr align="center">
        <td>—</td>
        <td><code>float()</code></td>
        <td>číselný</td>
      </tr>
      <tr align="center">
        <td>—</td>
        <td><code>complex()</code></td>
        <td>číselný</td>
      </tr>
      <tr align="center">
        <td>—</td>
        <td><code>str()</code>, <code>''</code>, <code>""</code></td>
        <td>sekvenční</td>
      </tr>
      <tr align="center">
        <td><code>bytearray()</code></td>
        <td><code>bytes()</code>, <code>b''</code>, <code>b""</code></td>
        <td>sekvenční</td>
      </tr>
      <tr align="center">
        <td><code>list()</code>, <code>[]</code></td>
        <td><code>tuple()</code>, <code>()</code>, <code>,</code></td>
        <td>sekvenční</td>
      </tr>
      <tr align="center">
        <td>—</td>
        <td><code>range()</code></td>
        <td>sekvenční</td>
      </tr>
      <tr align="center">
        <td><code>set()</code></td>
        <td><code>frozenset()</code></td>
        <td>množinový</td>
      </tr>
      <tr align="center">
        <td><code>dict()</code>, <code>{}</code></td>
        <td>—</td>
        <td>mapovací</td>
      </tr>
    </tbody>
  </table>
  
  <p>
    PS: Konstruktory základních typů volané funkcí jsou pomalejší než jejich přímé varianty – zatímco přímé variantě odpovídá přímo příslušný bajtkód, nepřímá zahrnuje standardní dohledání odpovídající funkce v seznamu jmen volatelných objektů a teprve poté její následné vykonání.
  </p>

</slide>
<slide title="Přiřazení (či spíše „pojmenovávání“)">

    <p>
        Naprosto základní věcí, se kterou se musí každý uživatel Python'u sžít, je:
    </p>
    <blockquote>
        Proměnné (<i>mutable</i>) typy jsou v Python'u předávány odkazem, neproměnné (<i>immutable</i>) hodnotou!
    </blockquote>
    <p>
        (Na začátku si na tom asi párkrát nabijete nos, ale časem na tuhle nakonec celkem logickou podivnost – zvanou, pokud vůbec nějak, <em>předávání objektem</em> – zvyknete.)
    </p>
    <p>
        Pro vysvětlení porovnejme kódy na následujících dvou slajdech.
    </p>

</slide>
<slide title="Přiřazení – proměnné typy">

  <p>
    Nejdříve se podíváme na typ proměnný (<em>mutable</em>) (zde zastoupený seznamem):
  </p>
  <example lang="python">
# A) zde 'xs' a 'ys' označují dva různé objekty
>>> xs = ['a', 'h', 'o', 'j']
>>> ys = ['a', 'h', 'o', 'j']

>>> xs.remove('o')
>>> xs
['a', 'h', 'j']
>>> ys
['a', 'h', 'o', 'j']

# B) zde jsou 'xs' a 'ys' pouze dvě různá pojmenování téhož objektu
>>> xs = ['a', 'h', 'o', 'j']
>>> ys = xs
>>> ys
['a', 'h', 'o', 'j']

>>> xs.remove('o')
>>> xs
['a', 'h', 'j']
>>> ys
['a', 'h', 'j']
  </example>

</slide>
<slide title="Přiřazení – neproměnné typy">

  <p>
    Podobná operace pro typ neproměnný (<em>immutable</em>, zde zastoupený řetězcem) dopadne ale takto:
  </p>
  <example lang="python">
# Zaveďme řetězcovou proměnnou 'xs'...
>>> xs = "jedna"
>>> xs
'jedna'
# ...a přidejme na ni alias pod jménem 'ys':
# (obě jména tedy ukazují na stejný objekt)
>>> ys = xs
>>> ys
'jedna'

# Změňme nyní hodnotu proměnné 'xs':
# (jméno 'xs' nyní odkazuje na jiný objekt, protože řetězec nemůžeme změnit)
>>> xs = "dva"
>>> xs
'dva'
# Proměnná 'ys' ovšem nadále odkazuje na původní objekt:
>>> ys
'jedna'
  </example>

</slide>
<slide title="Kopírování objektů I">

  <p>
    S předáváním proměnných z předchozího slajdu souvisí i rozdíl mezi <em>mělkou</em> a <em>hlubokou kopií</em> u složitějších objektů. Ukažme si to na slovnících a seznamech:
  </p>
  <example lang="python">
>>> xs = ['a', 'b', 'c']

# a) Zaveďme seznam a slovník, které obsahují podseznam..
>>> ys = [1, xs]
>>> zs = { 1: xs }
# ..a mělce je zkopírujme:
>>> ys2 = ys[:]
>>> zs2 = zs.copy()
# Výsledek je zatím nepřekvapivý:
>>> ys
[1, ['a', 'b', 'c']]
>>> ys2
[1, ['a', 'b', 'c']]
>>> zs
{1: ['a', 'b', 'c']}
>>> zs2
{1: ['a', 'b', 'c']}

# b) Odstraňme nyní z podseznamu poslední prvek:
>>> xs.pop()
'c'
>>> xs
['a', 'b']
# Nyní už se děje něco, co ne každý očekával:
>>> ys
[1, ['a', 'b']]
>>> ys2
[1, ['a', 'b']]
>>> zs
{1: ['a', 'b']}
>>> zs2
{1: ['a', 'b']}
  </example>

</slide>
<slide title="Kopírování objektů II">

  <p>
    Potřebujeme-li obejít uvedený problém, dává nám Python k dispozici metodu <code>copy.deepcopy()</code>:
  </p>
  <example lang="python">
>>> xs = ['a', 'b', 'c']

# a) Zaveďme seznam a slovník, které obsahují podseznam..
>>> ys = [1, xs]
>>> zs = { 1: xs }
# ..a hluboce je zkopírujme:
>>> import copy
>>> ys2 = copy.deepcopy(ys)
>>> zs2 = copy.deepcopy(zs)
# Výsledek je zatím nepřekvapivý:
>>> ys
0: [1, ['a', 'b', 'c']]
>>> ys2
1: [1, ['a', 'b', 'c']]
>>> zs
2: {1: ['a', 'b', 'c']}
>>> zs2
3: {1: ['a', 'b', 'c']}

# b) Odstraňme nyní z podseznamu poslední prvek:
>>> xs.pop()
4: 'c'
>>> xs
5: ['a', 'b']
# Nyní už se to vše chová úplně jinak:
>>> ys
6: [1, ['a', 'b']]
>>> ys2
7: [1, ['a', 'b', 'c']]
>>> zs
8: {1: ['a', 'b']}
>>> zs2
9: {1: ['a', 'b', 'c']}
  </example>

</slide>
<slide title="Poznámka">

    <p>
        Aby to nebylo tak průhledné – referencovaný objekt můžete klidně zrušit:
    </p>
    <example lang="python">
        # použijme v seznamu odkaz na jiný seznam
        >>> xs = ['a', 'b', 'c']
        >>> ys = [1, xs, 3]
        >>> ys
        [1, ['a', 'b', 'c'], 3]

        # jméno 'xs' použijeme pro jiný objekt, ale Python si zapamatuje, kam dříve odkazoval
        >>> xs = 666
        >>> ys
        [1, ['a', 'b', 'c'], 3]
    </example>
    <p>
        Pravda, pak se v něm už nemůžete „hrabat“, takže se zase vyhnete předchozím (možná nečekaným) efektům.
    </p>

</slide>
<!--slide title="Ad magické metody">
  
  <p>
  </p>
  <example lang="python">
  </example>

</slide-->
<slide title="Hešovatelnost I">

  <p>
    Proměnné (<em>mutable</em>) a neproměnné (<em>immutable</em>) datové typy se od sebe liší tím, že druhé z nich představují objekty s fixní hodnotou (nemohou být tedy změněny), zatímco prvé nikoli. Prakticky to znamená, že <strong>neproměnné typy jsou hešovatelné</strong> (<em>hashable</em>), což obnáší:
  </p>
  <ol>
    <li>
        dají se převést na svoji heš-hodnotu, která se během jejich existence nikdy nezmění
        <note>
          Tuto hodnotu můžeme zjistit pomocí funkce <code>hash(OBJEKT)</code>. Tu na pozadí zajišťuje „magická“ metoda <code>__hash__()</code>.
        </note>
    </li>
    <li>
        dají se porovnávat s dalšími objekty
        <note>
          Porovnatelnost zjišťují porovnávací operátory, přičemž na pozadí stojí „magická“ metoda <code>__eq__()</code>.
        </note>
    </li>
  </ol>
  <p>
    Hešovatelnost tak umožňuje použít neproměnné typy jako prvky <em>množin</em> a klíče <em>slovníků</em>. 
  </p>

</slide>
<slide title="Hešovatelnost II">

  <p>
    Několik poznámek:
  </p>
  <ul>
    <li>
        Heš-hodnoty jsou celá čísla.
    </li>
    <li>
        Rozdílné číselné typy stejné hodnoty mají samozřejmě stejnou heš (aby se porovnávaly stejně):
    </li>
  </ul>
  <example lang="python">
>>> x, y = 1, 1.0
>>> type(x)
&lt;class 'int'>
>>> type(y)
&lt;class 'float'>
>>> hash(x) == hash(y)
True
  </example>
  <ul>
    <li>
        Zdánlivě velmi podobná funkce <code>id(OBJEKT)</code> vrací „identifikátor“ objektu. Ten je po dobu života objektu jedinečný a konstantní, ale dva různé objekty existující v navzájem se nepřekrývajícím čase ho mohou mít stejný.
    </li>
    <li>
        Instance uživatelem definovaných tříd jsou z principu hešovatelné – porovnávají se jako nerovné a jejich heš-hodnotou je jejich <em>id()</em>.
    </li>
  </ul>

</slide>
<slide title="Náročnost operací">

  <!--p>
    Seznamy, n-tice a slovníky jsou implementovány jako heše, řetězce jako pole. To má mimo jiné následující vliv na náročnost operací prováděných nad příslušnými datovými strukturami:
  </p-->
  <p>
    Vzhledem k interní stavbě základních datových struktur Python'u platí pro náročnost operací následující:
  </p>
  <ul>
    <li>
        seznamy, n-tice, řetězce:
        <ol>
          <li>náhodný přístup: O(1)</li>
          <li>dotaz na prvek (<code>in</code>): O(n)</li>
          <li>vložení/smazání prvku (pouze pro seznamy): O(n)</li>
        </ol>
    </li>
    <li>
        slovníky, množiny:
        <ol>
          <li>náhodný přístup: O(1)</li>
          <li>dotaz na prvek (<code>in</code>): O(1)</li>
          <li>vložení/smazání prvku: O(1)</li>
        </ol>
    </li>
  </ul>
  <p>
    Kromě toho u slovníků (a množin) neexistuje lineární uspořádání prvků.
  </p>
  <notes>
      <note>
        <a href="http://www.cis.upenn.edu/~lhuang3/cse399-python/" class="external">http://www.cis.upenn.edu/~lhuang3/cse399-python/</a>
      </note>
      <note>
        <cite>Rance D. Necaise: „Data Structures and Algorithms Using Python“. John Wiley &amp; Sons, 2011.</cite>
      </note>
  </notes>
  <handout>
    Je to ještě pravda? V trojkovém Pythonu jsou přece řetězce unicodové a ty si uchované jako pole sice dokážu představit, i když každý znak může zabírat proměnný počet bajtů, ale...
  </handout>

</slide>
<slide title="Čísla">

  <p class="enumerate">
    Vynecháme-li rozšiřující moduly, máme v základním Python'u k dispozici tři číselné typy s odpovídajícími konverzními funkcemi:
  </p>
  <ul>
    <li>
        <code>int()</code> – celá čísla (s libovolnou přesností)
    </li>
    <li>
        <code>float()</code> – reálná čísla (s přesností aktuální implementace)
    </li>
    <li>
        <code>complex()</code> – komplexní čísla (jako dvojice reálných čísel)
    </li>
  </ul>
  
  <p class="enumerate">
    Uvedené v podstatě konstruktory neproměnných číselných typů mají jednu šikovnou vlastnost – zavolány bez argumentů vrací nulu v odpovídajícím typu:
  </p>
  <example lang="python">
>>> int()
0

>>> float()
0.0

>>> complex()
0j
  </example>

</slide>
<slide title="Čísla – zápis">

  <p>
    Celá a reálná čísla můžeme zapsat několika různými způsoby. U reálných je to ještě poměrně jednoduché..
  </p>
  <example lang="python">
>>> 3.14
3.14

>>> 3.14e2
314.0

>>> 3.14e-2
0.0314
  </example>
  <p>
    ..ale u celých je způsobů mnohem více:
  </p>
  <example lang="python">
# klasicky desítkově
>>> 26
26

# binárně
>>> 0b11010
26

# hexadecimálně
>>> 0x1a   # nebo 0x1A
26

# oktalově
>>> 0o32
26
  </example>
  <p>
    U komplexních čísel není moc co řešit – písmenka <code>j</code>, resp. <code>J</code>, slouží k označení imaginární části komplexního čísla.
  </p>

</slide>
<slide title="Čísla – převody">

  <p class="enumerate">
    Celá a reálná čísla na sebe můžeme převádět, stejně jako se můžeme pokusit vyextrahovat číslo z řetězce:
  </p>
  <example lang="python">
>>> float(1)
1.0
>>> int(1.2)
1

>>> float('1.23')
1.23
>>> int('23')
23
  </example>
  <note>
    Ale celé číslo z „reálného řetězce“ neuděláte:
    <example lang="python">
>>> int('1.23')
Traceback (most recent call last):
  File "&lt;pyshell#72>", line 1, in &lt;module>
    int('1.23')
ValueError: invalid literal for int() with base 10: '1.23'
    </example>
  </note>
  <p>
    PS: S komplexními čísly to takhle z pochopitelných důvodů nejde.
  </p>
  
  <p class="enumerate">
    Funkce <em>int()</em> dokáže zpracovat čísla v nejrůznějších základech:
  </p>
  <example lang="python">
>>> int("f", base=16)
    15
>>> int("10", base=16)
    16

>>> int("7", base=8)
    7
>>> int("10", base=8)
    8

>>> int("2", base=3)
    2
>>> int("10", base=3)
    3
  </example>

</slide>
<slide title="Řetězce vs bajtové objekty">

  <p class="enumerate">
    Řetězce obsahují unicodové znaky, tj. sekvence bajtů, které daným kódováním určují odkazy do tabulky unicodových znaků:
  </p>
  <example lang="python">
>>> for x in 'ahoj':
...     print(x)
a
h
o
j
  </example>
  
  <p class="enumerate">
    Bajtové řetězce na druhou stranu obsahují pouze bajty, tj. čísla 0-255:
  </p>
  <example lang="python">
>>> for x in b'ahoj':
...     print(x)
97
104
111
106
  </example>

</slide>
<slide title="Řetězce vs bajtové objekty – vzájemné převody">

  <p>
    Znakové a bajtové řetězce mezi sebou můžeme pomocí zvolených kódování navzájem převádět:
  </p>
  <example lang="python">
>>> xs = '狼.cz'
>>> len(xs)
4

# ukázka tří různých způsobů bajtového zakódování téhož řetězce
>>> xs.encode('utf-8')
b'\xe7\x8b\xbc.cz'
>>> len( xs.encode('utf-8') )
6
>>> xs.encode('gb18030')
b'\xc0\xc7.cz'
>>> len( xs.encode('gb18030') )
5
>>> xs.encode('big5')
b'\xafT.cz'
>>> len( xs.encode('big5') )
5

# řetězec → bajtový řetězec → řetězec
>>> xs.encode('big5').decode('big5')
'狼.cz'
  </example>

</slide>
<slide title="Řetězce vs bajtové objekty – bajtová pole">

  <p class="enumerate">
    Zatímco řetězce a bajtové řetězce jsou neměnné, tak k bajtovým řetězcům existuje proměnný protějšek – <em>bajtová pole</em>:
  </p>
  <example lang="python">
# A) řetězce
>>> xs = 'ahoj'
>>> xs[1]
19: 'h'
>>> xs[1] = 'H'
Traceback (most recent call last):
  File "&lt;pyshell#39>", line 1, in &lt;module>
    xs[1] = 'H'
TypeError: 'str' object does not support item assignment

# B) bajtové řetězce
>>> xs = b'ahoj'
>>> xs[1]
20: 104
>>> xs[1] = 'H'
Traceback (most recent call last):
  File "&lt;pyshell#42>", line 1, in &lt;module>
    xs[1] = 'H'
TypeError: 'bytes' object does not support item assignment

# C) bajtová pole
>>> xs = bytearray(b'ahoj')
>>> xs
21: bytearray(b'ahoj')
>>> xs[1]
22: 104
>>> xs[1] = 72
>>> xs
23: bytearray(b'aHoj')
  </example>

  <p class="enumerate">
    Řetězce i bajtové objekty typu <em>bytes</em> jsou neměnitelné a tudíž hešovatelné. Bajtová pole jako měnitelný protějšek bajtových řetězců ovšem nikoli:
  </p>
  <example lang="python">
>>> hash( 'ahoj' )
-1425894204
>>> hash( b'ahoj' )
1425894204

>>> hash( bytearray(b'ahoj') )
Traceback (most recent call last):
  File "&lt;pyshell#22>", line 1, in &lt;module>
    hash( bytearray(b'ahoj') )
TypeError: unhashable type: 'bytearray'
  </example>

</slide>
<slide title="N-tice vs seznamy">

  <p class="enumerate">
    Přirozeným konstruktorem prázdného seznamu je <code>[]</code>, seznam s jedním prvkem se zapíše jednoduše jako <code>[a,]</code>, přičemž ukončovací čárka není nutná.
  </p>
  <p>
    U n-tic to už není tak jednoduché: N-tice totiž „vyrábí“ operátor <code>,</code>, nikoli závorky. N-tice o jednom prvku se tak – kde to jde – zapisuje jako <code>a,</code>, případně se závorkami <code>(a,)</code>, jenže prázdnou n-tici získáme přímou notací jen a pouze jako <code>()</code> a žádná čárka tam dokonce být nesmí.
  </p>
  <note>
    Nebo můžete samozřejmě napsat <code>tuple()</code>, ale to je jasné.
  </note>
  
  <p class="enumerate">
    N-tice jsou všude prezentovány jako jakési „zamrzlé“ seznamy. Ve skutečnosti je mezi n-ticemi a seznamy poněkud hlubší rozdíl: Chcete-li kupříkladu zachytit souřadnice konkrétního bodu ve 3D-prostoru jako jeho jakýsi identifikátor (nebudou se tudíž měnit), je zcela přirozené použít pro to n-tici o třech prvcích, která jasně evokuje, že každá z položek n-tice má jistý konkrétní význam (souřadnice x-ová, y-ová a z-ová). Seznam by v tomto případě byl dosti zavádějící – sice by na jednu stranu umožňoval měnit souřadnice objektu, ale na druhou by také umožňoval měnit jejich počet, což má samozřejmě úplně jiné vyznění.
  </p>

</slide>
<slide title="Hešovatelnost n-tic">

  <p>
    N-tice jsou narozdíl od seznamů neměnné a tudíž hešovatelné (a tudíž použitelné jako prvky množin či jako klíče slovníků):
  </p>
  <example lang="python">
>>> ts = (1, 2, 3)
>>> hash(ts)
-378539185

>>> xs = [1, 2, 3]
>>> hash(xs)
Traceback (most recent call last):
  File "&lt;pyshell#3>", line 1, in &lt;module>
    hash(xs)
TypeError: unhashable type: 'list'
  </example>
  <p>
    Nesmíme to ovšem smíchat dohromady:
  </p>
  <example lang="python">
>>> xs = [1, 2, 3]
>>> ts = (1, 2, xs)
>>> ts
(1, 2, [1, 2, 3])
>>> type(ts)
&lt;class 'tuple'>
>>> hash(ts)
Traceback (most recent call last):
  File "&lt;pyshell#6>", line 1, in &lt;module>
    hash(ts)
TypeError: unhashable type: 'list'
  </example>
  <note>
    Algoritmus pro „výrobu“ heše jednoduše „cestou“ narazil ve třetí položce n-tice na nehešovatelný typ.
  </note>

</slide>
<slide title="N-tice jako vstupní parametry funkcí">

    <p>
        Zatímco skoro všude jsou následující dva zápisy n-tice ekvivalentní..
    </p>
    <example lang="python">
        prvek1, prvek2, prvek3
        (prvek1, prvek2, prvek3)
    </example>
    <p>
        ..uvnitř závorek při volání funkcí to už není pravda:
    </p>
    <example lang="python">
        funkce(  prvek1, prvek2, prvek3  )      # zavolej funkci se třemi parametry
        funkce( (prvek1, prvek2, prvek3) )      # zavolej funkci s jedním parametrem
    </example>

</slide>
<slide title="Množiny a slovníky">

  <p>
    O množinách můžeme celkem bez problémů uvažovat jako o slovnících bez hodnot – prvky množiny stejně jako klíče slovníku musí být jedinečné a navíc oboje neproměnného (a tudíž hešovatelné) typu. Přitom:
  </p>
  <ul>
    <li>
        Přirozeným konstruktorem prázdného slovníku je <code>{}</code>, slovník s jedním prvkem se zapíše jednoduše jako <code>{KLÍČ: HODNOTA, }</code>, přičemž ukončovací čárka není nutná.
    </li>
    <li>
        Vzhledem k historické volbě konstruktoru slovníku to s množinou už bohužel není tak jednoduché – prázdnou množinu tak získáme pouze pomocí konstruktoru <code>set()</code> a jinak to nejde. Delší množiny už jsou od slovníků snadno k rozeznání, protože neobsahují dvojtečku: <code>{PRVEK, }</code> (čárka při tom opět není povinná)
    </li>
  </ul>

</slide>
<slide title="Množiny a slovníky – hešovatelnost">

  <p class="enumerate">
    Narozdíl od dychotomie mezi n-ticemi a seznamy, kde souvislost „neměnná n-tice – proměnný seznam“ není zcela košer, u množin je to jednoduché: Potřebujeme-li neproměnnou množinu (a tudíž i hešovatelnou), využijeme konstrukturu <code>frozenset()</code>. Tuto „zamrzlou“ množinu je pak samozřejmě možné používat jako prvek jiných množin nebo klíč do slovníků.
  </p>

  <p class="enumerate">
    Slovníky a množiny jako proměnné typy hešovatelné nejsou, zmražené množiny jako typ neproměnný pak ano:
  </p>
  <example lang="python">
# zmražená množina
>>> hash( frozenset({1,}) )
593349751

# množina
>>> hash( {1,} )
Traceback (most recent call last):
  File "&lt;pyshell#58>", line 1, in &lt;module>
    hash( {1,} )
TypeError: unhashable type: 'set'

# slovník
>>> hash( {1: 1,} )
Traceback (most recent call last):
  File "&lt;pyshell#57>", line 1, in &lt;module>
    hash( {1: 1,} )
TypeError: unhashable type: 'dict'
  </example>

</slide>
<slide title="Mazání slovníků a množin">

    <p class="enumerate">
        Ačkoli operace <code>ds.clear()</code> a <code>ds = {}</code> poskytnou pro (předtím již existující) slovník <em>ds</em> stejný výsledek, je mezi nimi velký rozdíl z hlediska vnitřní (nejen paměťové) náročnosti obou operací:
    </p>
    <ul>
        <li>
            <code>ds.clear()</code> vezme existující slovník (tedy jisté místo v paměti, ke kterému se interpretr chová jako ke slovníku) a jednoduše ho vyprázdní.
        </li>
        <li>
            <code>ds = {}</code> nejdříve vyrobí nový prázdný slovník (tedy zabere úplně nové místo v paměti, které připraví tak, aby s ním mohl zacházet jako se slovníkem) a teprve poté přemapuje jméno z původního slovníku (který tak celý zůstane „viset“ v paměti, než ho při svém příštím běhu „smaže“ <em>garbage collector</em>) na tento nový.
        </li>
    </ul>
    <note>
        Proto při zadání..
        <example lang="python">
    >>> d1 = d2 = { 'a': 1, 'b': 123, }
    >>> d1
    {'a': 1, 'b': 123}
    >>> d2
    {'a': 1, 'b': 123}
        </example>
        ..metoda <em>clear()</em> smaže (v paměti) příslušný objekt, na nějž ukazují dvě jména..
        <example lang="python">
    >>> d1.clear()
    >>> d1
    6: {}
    >>> d2
    7: {}
        </example>
        ..ale přiřazení jména prázdnému slovníku dopadne pochopitelně úplně jinak:
        <example lang="python">
    >>> d1 = {}
    >>> d1
    {}
    >>> d2
    {'a': 1, 'b': 123}
        </example>
    </note>
    
    <p class="enumerate">
        Úplně to samé platí pro množiny, které nepřekvapivě mají metodu <em>clear()</em> také k dispozici.
    </p>

</slide>
<slide title="Rozsahy">

  <p class="enumerate">
    Rozsahy se konstruují pomocí funkce <code>range()</code>. Na první pohled se tváří jako sekvence, ale nepodporují výřezy (<em>slicing</em>), spojování (<em>concatenation</em>) ani opakování (<em>repetition</em>):
  </p>
  <example lang="python">
>>> xs = range(10)
>>> xs
range(0, 10)

>>> xs[5]
5

# výřez
>>> xs[:3]
Traceback (most recent call last):
  File "&lt;pyshell#50>", line 1, in &lt;module>
    xs[:3]
TypeError: sequence index must be integer, not 'slice'

# spojování
>>> xs + range(3)
Traceback (most recent call last):
  File "&lt;pyshell#51>", line 1, in &lt;module>
    xs + range(3)
TypeError: unsupported operand type(s) for +: 'range' and 'range'

# opakování
>>> 2 * xs
Traceback (most recent call last):
  File "&lt;pyshell#52>", line 1, in &lt;module>
    2 * xs
TypeError: unsupported operand type(s) for *: 'int' and 'range'
  </example>
  <note>
    Jinými slovy typ je to sice iterovatelný, ale nikoli sekvenční.
  </note>
  
  <p class="enumerate">
    Podobně používat na nich operátorů <code>in</code>, <code>not in</code> a funkcí <code>min()</code>, <code>max()</code> sice jde, ale není to moc efektivní, protože rozsah je kvůli nim třeba nejdříve vyrobit (což „poněkud“ nabourává jeho pojetí jako <em>líné</em> datové struktury, alespoň v Python'u 3.x):
  </p>
  <example lang="python">
>>> 6 in range(10)
True

>>> 10 in range(10)
False

>>> max( range(10) )
9

>>> min( range(10) )
0
  </example>

</slide>
<slide title="Vzájemné konverze typů">

  <p class="enumerate">
    Prakticky všechny iterovatelné typy se dají převádět navzájem na sebe. Pár příkladů:
  </p>
  <example lang="python">
>>> xs = 'ahoj'
>>> tuple(xs)
('a', 'h', 'o', 'j')
>>> list(xs)
['a', 'h', 'o', 'j']

>>> xs = 'ahoj ahoj'
>>> set(xs)
{'a', 'h', 'j', 'o', ' '}
  </example>
  
  <p class="enumerate">
    Převody mezi čísly a řetězci jsme už viděli na dřívějším slajdu:
  </p>
  <example lang="python">
>>> float(1)
1.0
>>> int(1.2)
1

>>> float('1.23')
1.23
>>> int('23')
23
  </example>

</slide>
<slide title="Složené typy I">

    <p>
        Jak jsme viděli, složitější datové typy (jako seznamy či slovníky) se v Python'u skládají z libovolných typů. Například zde jsou prvky slovníku seznamy:
    </p>
    <example lang="python">
        ds = {
            1: [1, 2, 3],
            2: [4, 5, 6],
        }
    </example>
    <p>
        Všimněte si následující důležité vlastnosti vybírání prvků (nejen) ze slovníku <em>ds</em>:
    </p>
    <example lang="python">
# Prvkem odpovídajícím klíči 1 slovníku je seznam „[1, 2, 3]“..
>>> ds[1]
   [1, 2, 3]

# ..a proto jeho prvkem na indexu 2 je číslo „3“:
>>> ds[1][2]
   3
    </example>
    <note>
        Komu to připadá na první pohled zmatené, rozložte si na chvilku uvedený kód takhle:
        <example lang="python">
            >>> prvek_slovníku_s_klíčem_1 = ds[1]
            >>> prvek_slovníku_s_klíčem_1
               [1, 2, 3]
            >>> prvek_slovníku_s_klíčem_1[2]
               3
        </example>
        „Fór“ je v tom, že pro adresování podtypů typů nemusíte žádné pomocné proměnné zavádět – prostě se rovnou odkážete na příslušný typ a následně pak jeho prvky.
    </note>

</slide>
<slide title="Složené typy II">

    <p>
        Toto <em>adresování</em> prvků typů v typech funguje samozřejmě pro libovolné typy a do libovolné hloubky:
    </p>
    <example lang="python">
# Seznam obsahující dvojce, jejichž prvky je číslo a řetězec:
>>> xs = [
...     (1, 'první'),
...     (2, 'druhý'),
...     (3, 'třetí'),
... ]

# Druhým prvkem (tedy prvkem na indexu 1) daného seznamu je uvedená dvojce:
>>> xs[1]
   (2, 'druhý')

# Druhým prvkem (tedy prvkem na indexu 1) dané dvojce je uvedený řetězec:
>>> xs[1][1]
   'druhý'

# Druhým prvkem (tedy prvkem na indexu 1) daného řetězce je uvedené písmeno:
>>> xs[1][1][1]
   'r'
    </example>

</slide>
<slide title="Poznámka k iterátorům">

  <p>
    Mimochodem – dejte si pozor, abyste si omylem neměnili datovou strukturu pod rukama, když nechcete ^_~
  </p>
  <example layout="vertical">
    <program src="_files/poznamka.py" lang="python"/>
    <out src="_files/poznamka.out" lang="text"/>
  </example>
  <p>
    „Chudák“ iterátor, který zajišťuje průchod po prvcích řetězce, pečlivě v každém kole přičte k poslednímu indexu jedničku, ale nemá ani ponětí, že jste mu pod rukama vyměnili přiřazení indexů na prvky.
  </p>

</slide>
<!--slide title="Modul „collections“">
  
  <p>
  </p>
  <example lang="python">
  </example>

</slide>
<slide title="Další zabudované typy">
  
  <p>
  </p>
  <example lang="python">
  </example>

</slide-->


</lecture>
