previous up next hi end end

10. Entice a sety

  1. Entice a měnitelnost
  2. Enticové přiřazení
  3. Entice jako výstupní hodnoty
  4. Čisté funkce a modifikátory III
  5. Set a frozenset
  6. Glosář
  7. Cvičení

10.1 Entice a měnitelnost

Až dosud jsme poznali dva složené datové typy: řetězce, které jsou složené z liter či znaků, a seznamy, které se skládají z položek libovolného typu. Jeden z rozdílů, které jsme zaznamenali, je ten, že položky seznamu mohou být měněny, zatímco znaky řetězce být měněny nemohou. Jinými slovy, seznamy jsou měnitelné a řetězce jsou neměnitelné.

Existuje ještě další typ, zvaný entice (tuple), který je podobný seznamu – s tím, že je neměnitelný. Z hlediska skladby je entice seznam hodnot, oddělených čárkou:

>>> tuple = 'a', 'b', 'c', 'd', 'e'

I když to není nezbytné, je obvyklé uzavírat entice do oblých závorek.

>>> tuple = ('a', 'b', 'c', 'd', 'e')

Při tvorbě entice s jedinou položkou musíme připojit závěrečnou čárku:

>>> t1 = ('a',)
>>> type(t1)
<class 'tuple'>

Bez čárky by byl výraz 'a' považován překladačem Pythonu za řetězec.

>>> t2 = ('a')
>>> type(t2)
<class 'string'>

Odhlédneme-li od skladby, jsou operace s enticemi stejné jako operace se seznamy. Indexový operátor vybere položku z entice:

>>> tuple = ('a', 'b', 'c', 'd', 'e')
>>> tuple[0]
'a'

Úsekový operátor vybere rozsah položek:

>>> tuple[1:3]
'b', 'c')

Pokusíme-li se změnit některou z položek, dostaneme chybové hlášení:

>>> tuple[0] = 'A'
TypeError: 'tuple' object doesn't support item assignment

Ovšem, i když nemůžeme měnit položky entice, můžeme je nahradit jinou enticí:

>>> tuple = ('A',) + tuple[1:]
>>> tuple
'A', 'b', 'c', 'd', 'e')

10.2 Enticové přiřazení

Občas bývá užitečné vzájemně vyměnit hodnoty dvou proměnných. Při použití běžného příkazu přiřazení musíme použít dočasnou proměnnou. Záměnu a a b provedeme například takto: (a,b musí být definováno!)

>>> a = "ahoj"
>>> b = 5
>>> temp = a
>>> a = b
>>> b = temp
>>> print (a, b)
5 ahoj

Musíme-li to provádět často, je uvedený postup neohrabaný. Python poskytuje enticové přiřazení, které tento problém řeší elegantně:

>>> a, b = b, a

Na levé straně je entice proměnných, na pravé straně je entice hodnot. Každá hodnota je přiřazena své příslušné proměnné. Všechny výrazy na pravé straně se provedou před tím, než jsou přiřazeny. Tato vlastnost činí z enticového přiřazení mnohostranný nástroj.

Počet proměnných na levé straně musí zajisté být stejný jako na pravé straně:

>>> a, b, c, d = 1, 2, 3
ValueError: need more than 3 values to unpack

10.3 Entice jako výstupní hodnoty

Funkce mohou vracet entici jako výstupní hodnotu. Můžeme například napsat funkci, která zamění dva parametry:

def swap(x, y): 
    return y, x

Při používání tété funkce je zapotřebí jisté ostražitosti:

>>> a, b = 10,5
>>> swap(a, b) 
(5,10)                 # dostali jsme co jsme chtěli
>>> a,b
(10,5)                 # vstupní hodnoty jsme ale nezměnili
>>> a,b = swap(a, b)   # musíme o to výslovně požádat
>>> a,b
(5,10)

Enticové přiřazení uvnitř funce swap se v prostoru __main__ neprojeví.

Tato funkce případně neprovede to, co jsme si přáli. To je příklad sémantické (významové) chyby.

Přehlednější ukázkou použití entice pro výstupní hodnoty funkce je tento příklad:

from math import pi
def kruh(r):
    c = 2 * pi * r   obvod kruhu
    a = pi * r * r   plocha kruhu
    return (r, c, a)

10.4 Čisté funkce a modifikátory III

V kapitole 9 jsme si povídali o čistých funkcích a modifikátorech v souvislosti se seznamy. Pro entice modifikátory psát nemůžeme, protože entice jsou neměnitelné. .

Zde máme modifikátor, který vloží novou hodnotu do středu seznamu:

#
# seqtools.py
#

def insert_in_middle(val, lst):
    middle = int(len(lst)/2)
    lst[middle:middle] = [val]

Spustíme si to, abychom viděli, že to chodí:

>>> from seqtools import *
>>> my_list = ['a', 'b', 'd', 'e']
>>> insert_in_middle('c', my_list)
>>> my_list
['a', 'b', 'c', 'd', 'e']

Zkusíme-li to s enticí, dostaneme chybu:

>>> my_tuple = ('a', 'b', 'd', 'e')
>>> insert_in_middle('c', my_tuple)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "seqtools.py", line 7, in insert_in_middle
    lst[middle:middle] = [val]
TypeError: 'tuple' object does not support item assignment
>>> 

Problém je ten, že entice jsou neměnitelné a nepodporují úsekové přiřazení. Jednoduchým řešením je udělat z insert_in_middle čistou funkci:

def insert_in_middle(val, tup):
    middle = int(len(tup)/2)
    return tup[:middle] + (val,) + tup[middle:]

Tato verze nyní chodí pro entice, ne však pro seznamy a řetězce. Chceme-li verzi pro všechny sekvenční typy, potřebujeme zapouzdřit naši hodnotu do správného sekvenčního typu. Malá pomocná funkce vykoná div:

def encapsulate(val, seq):
    if type(seq) == type(""):
        return str(val)
    if type(seq) == type([]):
        return [val]
    return (val,)

Nyní můžeme napsat insert_in_middle tak, aby pracovala s každým vestavěným typem sekvence:

def insert_in_middle(val, seq):
    middle = int(len(seq)/2)
    return seq[:middle] + encapsulate(val, seq) + seq[middle:]

Poslední dvě verze insert_in_middle jsou čisté funkce. Nemají žádné postranní účinky. Přidáme-li encapsulate a poslední verzi insert_in_middle do modulu seqtools.py, můžeme to vyzkoušet:

>>> from seqtools import *
>>> my_string = 'abde'
>>> my_list = ['a', 'b', 'd', 'e']
>>> my_tuple = ('a', 'b', 'd', 'e')
>>> insert_in_middle('c', my_string)
'abcde'
>>> insert_in_middle('c', my_list)
['a', 'b', 'c', 'd', 'e']
>>> insert_in_middle('c', my_tuple)
('a', 'b', 'c', 'd', 'e')
>>> my_string
'abde'

Hodnoty my_string, my_list, a my_tuple se nezměnily. Chceme-li, aby je fce insert_in_middle změnila, musíme výstup funkce přiřadit proměnné:

>>> my_string = insert_in_middle('c', my_string)
>>> my_string
'abcde' 

10.5 Set a frozenset

Set (množina) je neuspořádaná měnitelná kolekce odlišných prvků, frozenset je neměnitelná množina odlišných prvků. Používají se při testování "členství" a při eliminaci duplikátních zápisů.

Struktura set, frozenset podporuje množinové operace sjednocení (union), průnik (intersection), rozdíl (difference) a doplněk (symmetric_difference), funkci len(s) a idiomy x in s, x not in s.

Set vytvoříme buď výčtem nebo pomocí funkce set(), frozenset vytvoříme funkcí frozenset(). Obě funkce přijímají pouze jeden argument, jímž může být seznam, řetězec, entice i slovník. Pozice jednotlivých prvků není indexována.

>>> basket = {'apple','orange','apple','pear',orange','banana'}
>>> basket
{'orange', 'apple', 'banana', 'pear'}      # žádné duplikáty!
>>> tup = (1,2,3,"alpha",2)
>>> st = set(tup)
>>> st
{1, 2, 3, 'alpha'}                         # žádné duplikáty!
>>> fr = frozenset('alcatraz')
>>> fr 
frozenset({'a', 'c', 'l', 'r', 't', 'z'})  # žádné duplikáty!

Obě struktury mají definovanou částečně společnou řadu metod:

set_meth = add, clear, copy, difference, difference_update, 
discard, intersection, intersection_update, isdisjoint, issubset, 
issuperset, pop, remove, symmetric_difference,  union, update,
symmetric_difference_update

frozenset_meth = copy, difference, intersection, isdisjoint, 
issubset, issuperset, symmetric_difference,  union 

Metodami add, clear, discard, pop, remove a update můžeme měnit obsah setu.

Ukážeme si některé operace na setech:

>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a, b
({'a', 'c', 'b', 'r', 'd'}, {'a', 'c', 'z', 'm', 'l'})
>>> a - b          # rozdíl  
{'r', 'd', 'b'}    # b-a --> {'z', 'm', 'l'}
>>> a | b          # sjednocení
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b          # průnik
{'a', 'c'}
>>> a ^ b          # doplněk
{'r', 'd', 'b', 'm', 'z', 'l'}

Stejně jako pro seznam lze i pro set definovat komprehenci setu:

>>> s = {v for v in "ABCDABCD" if v not in "CB"}
>>> s
>>> {'A', 'D'}

10.6 Glosář

neměnitelný typ (immutable type)
Typ, jehož položky nemohou být měněny. Přiřazení k položkám či úsekům neměnitelných typů vyvolá chybu.
měnitelný typ (mutable type)
Datový typ, jehož položky mohou být měněny. Všechny měnitelné typy jsou složené. Seznamy a slovníky jsou měnitelné datové typy, řetězce a entice jsou neměnitelné.
entice (tuple)
Typ s uspořádanými položkami jako u seznamu, zde však jsou položky neměnitelné. Entice mohou být použity všude tam, kde je požadován neměnitelný typ, jako v klíči u slovníku.
enticové přiřazení (tuple assignment)
Přiřazení všem elementům entice jediným příkazem k přiřazení. Enticové přiřazení se realizuje paralelně, takže je vhodné pro záměnu hodnot.
deterministický
Program, který při každém volání provádí totéž.
pseudo-náhodné číslo
Pořadí čísel zdánlivě náhodných, které jsou však ve skutečnosti výsledkem deterministického výpočtu.
histogramm
Seznam celých čísel, udávajících četnost výskytu nějaké skutečnosti.
srovnávání vzorů (pattern matching)
Vývojová strategie při psaní programu, při které je použito ověřené řešení podobného problému.

10.7 Cvičení

  1. Nakreslete schéma vztahů uvedené funkce z něhož bude patrné, proč funkce nepracuje.
    def swap (x, y):
        x, y = y, x
    
  2. Generujte náhodné číslo mezi low a high.
  3. Generujte náhodné celé číslo mezi low a high včetně.
  4. Zapište příklad z 11.8 jako funkci histogram a vyzkoušejte volání pro n = 2000, 4000, 8000, b = 8. Ověřte, zda četnosti spějí k rovnoměrnénu výskytu.
  5. te modul seqtools.py. Přidejte funkce encapsulate a insert_in_middle probírané v odstavci 11.9. Přidejte doctesty, které ověří, zda uvedené funkce pracují jak zamýšleno pro všechy tři sekvenční typy.
  6. Do souboru seqtools.py doplňte následující funkce s vyhovujícími doctesty:
    def make_empty(seq):
        """
        >>> make_empty([1, 2, 3, 4])
        []
        >>> make_empty(('a', 'b', 'c'))
        ()
        >>> make_empty("No, not me!")
        ''
        """
    
    def insert_at_end(val, seq):
        """
        >>> insert_at_end(5, [1, 3, 4, 6])
        [1, 3, 4, 6, 5]
        >>> insert_at_end('x', 'abc')
        'abcx'
        >>> insert_at_end(5, (1, 3, 4, 6))
        (1, 3, 4, 6, 5)
        """
    
    def insert_in_front(val, seq):
        """
        >>> insert_in_front(5, [1, 3, 4, 6])
        [5, 1, 3, 4, 6]
        >>> insert_in_front(5, (1, 3, 4, 6))
        (5, 1, 3, 4, 6)
        >>> insert_in_front('x', 'abc')
        'xabc'
        """
    
    def index_of(val, seq, start=0): """ >>> index_of(9, [1, 7, 11, 9, 10]) 3 >>> index_of(5, (1, 2, 4, 5, 6, 10, 5, 5)) 3 >>> index_of(5, (1, 2, 4, 5, 6, 10, 5, 5), 4) 6 >>> index_of('y', 'happy birthday') 4 >>> index_of('banana', ['apple', 'banana', 'cherry', 'date']) 1 >>> index_of(5, [2, 3, 4]) -1 >>> index_of('b', ['apple', 'banana', 'cherry', 'date']) -1 """
    def remove_at(index, seq): """ >>> remove_at(3, [1, 7, 11, 9, 10]) [1, 7, 11, 10] >>> remove_at(5, (1, 4, 6, 7, 0, 9, 3, 5)) (1, 4, 6, 7, 0, 3, 5) >>> remove_at(2, "Yomrktown") 'Yorktown' """
    def remove_val(val, seq): """ >>> remove_val(11, [1, 7, 11, 9, 10]) [1, 7, 9, 10] >>> remove_val(15, (1, 15, 11, 4, 9)) (1, 11, 4, 9) >>> remove_val('what', ('who', 'what', 'when', 'where', 'why', 'how')) ('who', 'when', 'where', 'why', 'how') """
    def remove_all(val, seq): """ >>> remove_all(11, [1, 7, 11, 9, 11, 10, 2, 11]) [1, 7, 9, 10, 2] >>> remove_all('i', 'Mississippi') 'Msssspp' """ def count(val, seq): """ >>> count(5, (1, 5, 3, 7, 5, 8, 5)) 3 >>> count('s', 'Mississippi') 4 >>> count((1, 2), [1, 5, (1, 2), 7, (1, 2), 8, 5]) 2 """ def reverse(seq): """ >>> reverse([1, 2, 3, 4, 5]) [5, 4, 3, 2, 1] >>> reverse(('shoe', 'my', 'buckle', 2, 1)) (1, 2, 'buckle', 'my', 'shoe') >>> reverse('Python') 'nohtyP' """ def sort_sequence(seq): """ >>> sort_sequence([3, 4, 6, 7, 8, 2]) [2, 3, 4, 6, 7, 8] >>> sort_sequence((3, 4, 6, 7, 8, 2)) (2, 3, 4, 6, 7, 8) >>> sort_sequence("nothappy") 'ahnoppty' """ if __name__ == "__main__": import doctest doctest.testmod()
  7. Podle výčtů v odstavci 10.5 vytvořte sety set_meth a frozenset_meth, které použijete pro zkoumání jejich odlišností.

previous up next hi end end