previous up next hi end end

8. Seznamy I

  1. Vytvoření seznamu
  2. Přístup k položkám
  3. Délka seznamu
  4. Příslušnost položky
  5. Operace se seznamy
  6. Úseky seznamu
  7. Seznamy jsou měnitelné
  8. Smazání položek
  9. Identita objektů
  10. Další jméno - alias
  11. Klonování seznamů
  12. Funkce range()
  13. Seznamy a smyčky 'for'
  14. Komprehence seznamu
  15. Metamorfózy
  16. Seznam jako argument
  17. Čisté funkce a modifikátory II
  18. Glosář
  19. Cvičení

Seznam (list) je uspořádaná kolekce hodnot libovolného typu. Jednotlivým hodnotám říkáme položky a tyto položky jsou měnitelné. Seznamy, řetězce a jiné entity, které se chovají jako uspořádané řady, nazýváme sekvence.

8.1 Vytvoření seznamu

Taxativním výčtem

Nový seznam vytvoříme nejjednodušeji uzavřením položek do hranatých závorek:

[10, 20, 30, 40]
["spam", "bungee", "swallow"]

Prvním příkladem je seznam čtyř celých čísel. Druhým je seznam tří řetězců. Položky seznamu nemusí být stejného typu. Následující seznam obsahuje řetězec, float (číslo v plovoucí řádové čárce), integer (celé číslo) a (mirabile dictu!) další seznam:

["hello", 2.0, 5, [10, 20]]

Seznamu uvnitř jiného seznamu říkáme, že je vnořený.

Konečně, existuje speciální seznam, který neobsahuje žádné položky. Nazývá se prázdný seznam a značí se [ ].

Stejně jako číselná hodnota 0 a prázdný řetězec, je prázdný seznam nepravdivý v booleovských výrazech:.

>>> bool(["hello", 2.0])
True
>>> bool([])
False
>>>

Při všech těchto možnostech tvorby seznamů by bylo zklamáním, kdybychom nemohli přiřadit seznam hodnot k proměnné, nebo zadat seznam jako parametr či argument funkce. Věc se má tak, že můžeme.

>>> vocabulary = ["ameliorate", "castigate", "defenestrate"]
>>> numbers = [17, 123]
>>> empty = []
>>> print(vocabulary, numbers, empty)
['ameliorate', 'castigate', 'defenestrate'] [17, 123] []

Funkcí list()

Funkce list(arg) přijímá jeden argument, jímž může být kontejner typu string, list, tuple, set, dict   a z prvků těchto kontejnerů vytvoří seznam

str = "afrodité"                        # řetězec
lst = ["pí", 3.14, 8]                   # seznam
tup = ("g", 9.89, 8)                    # entice
set = {"e", 2.72, 8}                    # množina
dct = {"pí":3.14, "g": 9.89, "e":2.72}  # slovník

Tyto proměnné postupně použijeme jako argumenty funkce:

>>> list(str)
['a', 'f', 'r', 'o', 'd', 'i', 't', 'é']
>>>
# další volání si zkuste sami

8.2 Přístup k položkám

Syntaxe pro přístup k položkám seznamu je stejná jako syntaxe pro přístup ke znakům řetězce – pomocí hranatých závorek s indexem. Nezapomeňte, že indexy začínají nulou:

>>> print(numbers[0])
17

Jako index lze použít jakýkoli celočíselný výraz:

>>> numbers[9-8]  #  = 1
123
>>> numbers[1.0]
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: list indices must be integers    

Pokusíme-li se číst nebo psát položku, která neexistuje, dostaneme chybu při běhu programu:

>>> numbers[2]
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
IndexError: list index out of range   

Má-li index zápornou hodnotu, počítá se od konce seznamu:

>>> numbers[-1]
123
>>> numbers[-2]
17
>>> numbers[-3]
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
IndexError: list index out of range    

numbers[-1] je poslední položka seznamu, numbers[-2] je předposlední a numbers[-3] neexistuje.

Je obvyklé použít proměnnou smyčky jako index seznamu:

horsemen = ["war", "famine", "pestilence", "death"]

i = 0                  # proměnná smyčky
while i < 4:
    print(horsemen[i])
    i = i + 1

Tato smyčka počítá od 0 do 4. Jakmile má proměnná smyčky hodnotu 4, podmínka nevyhoví a smyčka končí. Takže tělo smyčky je provedeno pro i = 0, 1, 2 a 3.

Při každém cyklu smyčky je proměnná i použita jako index položky, která se tiskne. Tento způsob výpočtu se nazývá traverzování seznamem.

8.3 Délka seznamu

Funkce len vrací délku seznamu, což je počet jeho položek. Tato hodnota se výhodně používá jako horní mez smyčky místo konstanty. Tím si zajistíme, že pokaždé, když se změní velikost seznamu, nemusíme procházet programem, abychom opravili všechny dotčené smyčky:

horsemen = ["war", "famine", "pestilence", "death"]

i = 0
while i < len(horsemen):
    print( horsemen[i] )
    i = i + 1

Po posledním provedením těla smyčky je i=len(horsemen)-1, což je index poslední položky. Je-li i=len(horsemen), podmínka není splněna a tělo se neprovede, což je dobré, protože len(horsemen) není přípustný index.

I když jeden seznam může obsahovat další seznam, ten vnořený se stále počítá jako jedna položka. Délka tohoto seznamu je 4:

['spam!', 1, ['Brie', 'Roquefort', 'Pol le Veq'], [1, 2, 3]]

8.4 Příslušnost položky

Klíčové slovo in je booleovský operátor, který testuje příslušnost prvku k sekvenci. Použili jsme jej už u řetězců a pracuje také se seznamy a s jinými uspořádanými množinami:

>>> horsemen = ['war', 'famine', 'pestilence', 'death']
>>> 'pestilence' in horsemen
True
>>> 'debauchery' in horsemen
False

Protože pestilence je částicí seznamu horsemen, vrátí operátor in hodnotu True. Jelikož debauchery v seznamu není, vrátí False.

Můžeme použít slovo not ve spojení s in, abychom ověřili, že položka není částicí seznamu:

>>> 'debauchery' not in horsemen
True

8.5 Operace se seznamy

Společné operace pro seznamy a řetězce

U seznamu lze uplatnit stejné operace jako u řetězců - viz 8.5.

Operátor + zřetězí seznamy:

>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> c = a + b
>>> print( c )
[1, 2, 3, 4, 5, 6]

Podobně, operátor * opakuje seznam v zadaném počtu:

>>> [0]*4
[0, 0, 0, 0]
>>> [1, 2, 3]*3
[1, 2, 3, 1, 2, 3, 1, 2, 3]

První příklad opakuje [0] čtyřikrát. Druhý příklad opakuje seznam [1, 2, 3] třikrát.

Vestavěné metody pro seznamy

Kromě společných procedur pro seznamy a řetězce existují rovněž předdefinované funkce, použitelné pouze pro seznamy, které většinou evokujeme formátem lst.metoda():

append, clear, copy, count, extend, index, insert, pop,
remove, reverse, sort
a dále specielní metody, spojené se zdvojenými podtržítky:
__{add, class, contains, delatr, delitem, dir, doc, eq, format, getattribute, getitem, ge, qt, hash, iadd, imul, init, iter, len, le, lt, mul, new, ne, reduce_ex, reduce, repr, reversed, rmul, setattr, setitem, sizeof, str, subclasshook}__

Podrobnosti o metodách opět nejlépe zjistíme v editoru PyScripter při zápisu >>> muj_list . . U některých specielních metod se dozvíme, že je lze volat alternativním způsobem.

Pro seznamy neexistuje modul list jako existuje modul string pro řetězce.

8.6 Úseky seznamu

Operace s úseky, které jsme poznali u řetězců, platí také u seznamů:

>>> a_list = ['a', 'b', 'c', 'd', 'e', 'f']
>>> a_list[1:3]
['b', 'c']
>>> a_list[:4]
['a', 'b', 'c', 'd']
>>> a_list[3:]
['d', 'e', 'f']
>>> a_list[:]
['a', 'b', 'c', 'd', 'e', 'f']

8.7 Seznamy jsou měnitelné

Na rozdíl od řetězců jsou seznamy měnitelné, což znamená, že můžeme měnit jejich položky. Použitím závorkového operátoru [ ] na levé straně přiřazení můžeme měnit hodnotu položek:

>>> fruit = ["banana", "apple", "quince"]
>>> fruit[0] = "pear"
>>> fruit[-1] = "orange"
>>> print(fruit)
['pear', 'apple', 'orange']

Přiřazení hodnoty položce se nazývá položkové přiřazení (item assignment). Takovéto přiřazení nejde použít u řetězců:

>>> my_string = 'TEST'
>>> my_string[2] = 'X'
Traceback (most recent call last):
  File "<stdin >", line 1, in <module > 
TypeError: 'str' object does not support item assignment 
(objekt 'str' nepodporuje položkové přiřazení)

ale podporuje je pro seznamy:

>>> my_list = ['T', 'E', 'S', 'T']
>>> my_list[2] = 'X'
>>> my_list
['T', 'E', 'X', 'T']

Úsekovým operátorem můžeme změnit několik položek najednou:

>>> a_list = ['a', 'b', 'c', 'd', 'e', 'f']
>>> a_list[1:3] = ['x', 'y']
>>> print(a_list)
['a', 'x', 'y', 'd', 'e', 'f']

Můžeme také odstranit položky ze seznamu tím, že je nahradíme prázdným seznamem:

>>> a_list = ['a', 'b', 'c', 'd', 'e', 'f']
>>> a_list[1:3] = []
>>> print(a_list)
['a', 'd', 'e', 'f']

A můžeme také přidat položky do seznamu vmáčknutím do prázdného úseku v potřebném místě:

>>> a_list = ['a', 'd', 'f']
>>> a_list[1:1] = ['b', 'c']
>>> print(a_list¨)
['a', 'b', 'c', 'd', 'f']
>>> a_list[4:4] = ['e']
>>> print(a_list)
['a', 'b', 'c', 'd', 'e', 'f']

8.8 Smazání položek

Použití úseků k výmazu položek je neohrabané a proto náchylné k chybám. Python poskytuje šikovnější alternativu.

Příkaz del odstraní položku ze seznamu:

>>> a = ['one', 'two', 'three']
>>> del a[1]
>>> a
['one', 'three']

Nepřekvapí nás, že del manipuluje také se zápornými indexy a vyvolá chybu při běhu programu, je-li index mimo dovolený rozsah.

Jako indexy pro del můžeme použít také úseky:

>>> a_list = ['a', 'b', 'c', 'd', 'e', 'f']
>>> del a_list[1:5]
>>> print(a_list)
['a', 'f']

Jako obvykle, úseky vyberou všechny položky zadaného rozsahu, kromě horní meze.

8.9 Identita objektů

Provedeme-li tyto příkazy přiřazení,

a = "banana"
b = "banana"

víme, že a a b poukazují na řetězec s písmeny "banana". Není však zřejmé, zda se jedná o tentýž řetězec.

Jsou dvě možné situace:

V prvním případě odkazují a a b ke dvěma různým věcem, které mají stejnou hodnotu. Ve druhém případě odkazují k téže věci. Těmto "věcem" říkáme objekty. Objekt je něco, na co může proměnná odkazovat.

Každý objekt má jedinečný identifikátor, k němuž máme přístup prostřednictvím funkce id. Jeho vytištěním můžeme zjistit, zda se obě proměnné a i b odkazují ke stejnému objektu:

>>> id(a)
135044008
>>> id(b)
135044008

Dostali jsme dvakrát stejný identifikátor, což znamená, že Python vytvořil pouze jeden řetězec a jak a, tak b se k němu odkazují.

Seznamy se chovají odlišně od řetězců. Souvisí to s tím, že seznamy jsou měnitelné kolektory, zatímco řetězce (a entice) jsou neměnitelné kolektory. Vytvoříme-li dva seznamy, dostaneme dva objekty:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a) == id(b)
False

Zobrazení vztahů vypadá tedy takto:

Proměnné a a b mají stejnou hodnotu ale nevztahují se ke stejnému objektu.

8.10 Další jméno - alias

Proměnné odkazují k objektům; přiřadíme-li jednu proměnnou ke druhé, potom obě proměnné odkazují ke stejnému objektu:

>>> a = [1, 2, 3]
>>> b = a
>>> id(a) == id(b)
True

V tomto případě vypadá schema vztahů takto:

Protože tentýž seznam má dvě různá jména (a, b), říkáme, že je aliasován. Změny provedené s jedním aliasem se projeví i u druhého:

>>> b[0] = 5
>>> print(a)
[5, 2, 3]

I když toto chování může být užitečné, je někdy nečekané, ba přímo nežádoucí. Obecně vzato, pracujeme-li s měnitelnými objekty, je bezpečnější se aliasování vyhnout. S neměnitelnými objekty ovšem žádný problém není. Proto se řetězce aliasují, kdykoli je to vhodné.

8.11 Klonování seznamů

Chceme-li provést úpravy v seznamu a zároveň si uchovat kopii originálu, potřebujeme zkopírovat samotný seznam, nikoliv jen jeho odkaz. Tomuto procesu někdy říkáme klonování, abychom se vyhnuli dvojznačnosti slova "kopírování".

Nejjednodušší způsob klonování je užití úsekového operátoru:

>>> a = [1, 2, 3]
>>> b = a[:]
>>> a == b  # porovnáváme hodnoty
True
>>> a is b  # porovnáváme ID
False
>>> id(a) == id(b)
False
>>>

Nyní můžeme měnit b bez obav o a.

>>> b[0] = 5
>>> print(a)
[1, 2, 3]
>>> print(b)
[5, 2, 3]

8.12 Funkce 'range'

Vestavěná funkce range(n) vytvoří skrytou posloupnost čísel od nuly po n-1. Tuto posloupnost lze použít:

-- jako argument funce list při tvorbě seznamu :
>>> list(range (7))
[0, 1, 2, 3, 4, 5, 6]
>>> list(range (2,7))
[2, 3, 4, 5, 6]
>>> list(range (0,7,2))
[0, 2, 4, 6]
>>> list(range (7,5))
[]
>>> list(range (7,0,-3))
[7, 4, 1]
>>>
-- jako iterátorový objekt (viz 13.6) ve smyčce for:
>>> for i in(range(7))
...   print(i, end="  ")
... 
0, 1, 2, 3, 4, 5, 6
>>>

8.13 Seznamy a smyčky 'for'

Zobecněná skladba smyčky for je tato:

for VARIABLE in LIST:
    BODY    

Tento příkaz je rovnocenný zápisu:

i = 0
while i < len(LIST):
    VARIABLE = LIST[i]
    BODY
    i += 1   

Smyčka for je stručnější, protože se obejdeme bez proměnné i.

Použití smyčky for je velmi rozmanité:

for number in range(20):
    if number%3 == 0:
        print(number, end="  ")

for fruit in ["banana", "apple", "quince"]:
    print("I like to eat " + fruit +"s!") 

První příklad vytiskne všechny násobky 3 od 0 po 18. Druhý příklad vyjádří nadšení pro různé druhy ovoce.

Měnitelnost seznamu nám umožňuje upravit při traverzování každou jeho položku. Následující kód vytvoří druhé mocniny čísel od 1 do 5:

numbers = [1, 2, 3, 4, 5]
for index in range(len(numbers)):
    numbers[index] = numbers[index]**2
# o výsledek si musíme říci:
# >>> numbers

Zamyslete se nad příkazem range(len(numbers)) a snažte se pochopit, jak tento příkaz pracuje. Uvnitř seznamu nás zajímá jak hodnota položky, tak i její index.

Tento postup je natolik běžný, že Python nabízí k použití i jiný způsob:

numbers = [1, 2, 3, 4, 5]
for index, value in enumerate(numbers):
    numbers[index] = value**2

Funkce enumerate generuje při traverzování seznamem jak index, tak i odpovídající hodnotu. Pro lepší pochopení práce příkazu enumerate si vyzkoušejte následující příklad:

>>> for index, value in enumerate(['banana', 'apple', 'pear']):
...     print(index, value )
...
0 banana
1 apple
2 pear
>>>    

Příkazy break a continue se ve smyčkách for.. a while použijí pro seznamy (a entice) stejně jako u řetězců - viz kap. 7.8 a 7.9.

8.14 Komprehence seznamu

Komprehence seznamu je skladebný předpis pro vytvoření seznamu na základě jiné sekvence s použitím kompaktního, matematického předpisu, případně ještě při splnění jisté podmínky.

Matematický výraz:

S = { 2*x | x € N, x² > 3 }
lze slovně interpretovat asi takto: Pro všechna x z oboru přirozených čísel N jejichž druhé mocniny jsou větší než 3 se provede výraz 2*x.

V jazyce Python lze uvedený výraz vyjádřit konstrukcí, kterou si označíme jako komprehence seznamu (list comprehension):

>>> S = [f(x) for x in <sekvence> if <podmínka>]
Poslední část if <podmínka> není povinná, takže například:

>>> numbers = [1, 2, 3, 4]
>>> [x**2 for x in numbers]
[1, 4, 9, 16]
>>> [x**2 for x in numbers if x**2 > 8]
[9, 16]
>>>
>>> files = ['bin', 'Data', 'Desktop', '.bashrc', '.ssh', '.vimrc']
>>> [name for name in files if name[0] != '.']
['bin', 'Data', 'Desktop']
Funkčních předpisů může být v jedné komprehenci více:
>>> [(x, x**2, x**3) for x in numbers]
[(1, 1, 1), (2, 4, 8), (3, 9, 27), (4, 16, 64)]
Může být více i poskytnutých sekvencí:
>>> numbers = (1, 2, 3, 4)
>>> letters = ['a', 'b', 'c']
>>> [n*letter for n in numbers for letter in letters]
['a', 'b', 'c', 'aa', 'bb', 'cc', 'aaa', 'bbb', 'ccc', 'aaaa', 'bbbb', 'cccc']
>>>

Obecně lze komprehenci seznamu zapsat také takto:

[expr for item1 in seq1 for item2 in seq2 ... for itemx in seqx if condition]

Tento výraz má stejný účinek jako opakované použití smyčky for:

output_sequence = []
for item1 in seq1:
    for item2 in seq2:
        ...
            for itemx> in seqx:
                if condition:
                    output_sequence.append(expr)

Jak vidno, komprehence seznamu je mnohem kompaktnější.

Jako vstupní sekvenci lze v komprehenci seznamu použít i entici, případně řetězec:

>>> [v for v in "lopata"]
['l', 'o', 'p', 'a', 't', 'a']
Výsledným útvarem je seznam.

Podobně lze "komprehendovat" i set (na set):

>>> s = {v for v in "ABCDABCD" if v not in "CB"}
>>> s
>>> {'A', 'D'}
nebo slovník (na slovník):
>>> d = {key:val for key,val in enumerate('ABCD') if val not in "CB"}
>>> d
{0: 'A', 3: 'D'}

Hranaté (či složené) závorky na okrajích komprehencí jsou důležitým konstitučním prvkem!


8.15 Metamorfózy

Umíme z řetězce vytvořit seznam (list(arg)) a ze seznamu řetězec (str(arg)), dokonce to umíme udělat jedním tahem:

>>> str (list("Žabička"))
"['Ž', 'a', 'b', 'i', 'č', 'k', 'a']"

Chceme-li z rozsypaného čaje sestavit zase žabičku, musíme použít funkci join :

>>> frog = list("Žabička")
>>> frog
['Ž', 'a', 'b', 'i', 'č', 'k', 'a']
>>> "".join(frog)
'Žabička'
>>>

Funkce split rozloží řetězec na seznam slov. :

>>> song = "The rain in Spain..."
>>> song.split()
['The', 'rain', 'in', 'Spain... ']
>>> 

Vhodně vybraný argument, nazvaný oddělovač (separátor), může být použit jako hranice mezi oddělenými úseky. Následující příklad používá jako oddělovač řetězec ai:

>>> song.split(sep='ai')
['The r', 'n in Sp','n... ']
>>> 

Všimněte si, že zadaný separátor se ve vytvořeném seznamu neobjevuje.

Fce join i split přijímá dva argumenty: seznam řetězců a separátor. Hodnota separátoru může být implicitní - split()

>>> words = ['crunchy', 'raw', 'unboned', 'real', 'dead', 'frog']
>>> " ".join(words)
'crunchy raw unboned real dead frog'
>>> "**".join(words)
'crunchy**raw**unboned**real**dead**frog'

8.16 Seznam jako argument

Při zadávání seznamu jako argument se ve skutečnosti předává odkaz, nikoliv kopie seznamu. Protože jsou seznamy měnitelné, mohou být jeho položky použitou funkcí změněny. Například, následující funkce přijme seznam jako argument a vynásobí dvěma každou jeho položku:

def double_stuff_m(a_list):
    for index, value in enumerate(a_list):
        a_list[index] = 2 * value    

Vložíme-li double_stuff_m do souboru se jménem ch09_double.py, můžeme si postup vyzkoušet:

>>> from ch09_double import double_stuff_m
>>> things = [2,5,'spam',9.5]
>>> double_stuff_m(things)
>>> things
[4, 10, 'spamspam', 19.0]
>>>

8.17 Čisté funkce a modifikátory II

Funkce, které přijmou seznam jako argument a změní jej při provádění, se nazývají modifikátory a změnám které provedou na zadaném argumentu, se říká vedlejší účinky. Funkci s uvedenými vlastnostmi jsme si zapsali v předchozím odstavci.

Čistá funkce neprodukuje vedlejší účinky. Komunikuje s okolním programem pouze prostřednictvím argumentů které nepřetváří a prostřednictvím výstupní hodnoty. Zde je double_stuff_p jako čistá funkce:

 def  double_stuff_p ( a_list ):
     new_list = [] 
     for  value in a_list:
         new_list += [2*value]       
     return  new_list    

Funkce double_stuff_p nemění své argumenty:

>>> from ch09_double import double_stuff_p
>>> things = [2,5,'spam',9.5]
>>> double_stuff_p(things)
[4, 10, 'spamspam', 19.0]
>>> things
[2, 5, 'spam', 9.5]
>>>

Chceme-li aby čistá funkce double_stuff_p změnila seznam things, přiřadíme mu výstup z čisté funkce:

>>> things = double_stuff_p(things)
>>> things 
[4, 10, 'spamspam', 19.0]
>>>

Co je lepší?

Všechno, co může být provedeno pomocí modifikátorů, může být také provedeno čistými funkcemi. Některé programovací jazyky znají vlastně jenom čisté funkce. Má se zato, že čisté funkce se snadněji tvoří a jsou méně náchylné k chybám než programy, které používají modifikátory. Nicméně, modifikátory jsou někdy vhodné a v některých případech jsou programy s čistými funkcemi méně výkonné.

Doporučujeme tedy používat čisté funkce kdykoli je to účelné, a k modifikátorům se uchylovat jen při jejich zjevné výhodnosti. Tento přístup lze označit jako funkcionální programovací styl.

8.18 Glosář

seznam (list)
Měnitelná kolekce objektů (případně různého typu), kde každý objekt je označen indexem.
index
Celočíselná proměnná nebo hodnota, která označuje položku kolekce.
položka (element)
Jedna z hodnot seznamu (nebo jiné sekvence). Operátor z hranatých závorek vybere položku ze seznamu.
sekvence (sequence)
Jakýkoliv datový typ, skládající se z uspořadané řady položek, kde každá položka je určena indexem.
vnořený seznam (nested list)
Seznam, který je položkou jiného seznamu.
krok (step size)
Interval mezi sousedními položkami v lineární sekvenci. Také třetí (a nepovinný) argument fce range. Není-li zadán, jeho implicitní hodnotou je 1.
traverzování seznamem (list traversal)
Postupný výběr každé položky seznamu.
měnitelný datový typ (mutable data type)
Datový typ, jehož položky mohou být měněny. Všechny měnitelné typy jsou složené typy. Seznamy jsou měnitelné datové typy, řetězce nikoliv.
objekt (object)
Něco, na co může být odkazováno proměnnou.
alias
Vícero proměnných, odkazujících na stejný objekt.
klonovat (clone)
Vytvořit nový objekt, který má stejnou hodnotu jako stávající objekt. Kopírování odkazu na objekt vytvoří alias, nikoliv klon.
modifikátor (modifier)
Funkce, která změní zadané argumenty. Pouze měnitelné datové typy mohou být měněny modifikátory.
vedlejší účinek (side effect)
Změna položek seznamu, který byl zadán jako argument volané funkci. Tuto schopnost mají pouze modifikátory.
čistá funkce (pure funkction)
Funkce bez vedlejších účinků. Čistá funkce nemění hodnotu vstupních argumentů.
komprehence seznamu (list comprehension)
Určení položek seznamu nikoliv jejich výčtem ale popisem jejich vlastností.
testem podnícený rozvoj (test-driven development, TDD)
Postup při programování, při kterém se dospěje k žádanému cíli přes řadu malých, postupných kroků, jež jsou ověřovány jednotkovými testy.
oddělovač (separator, delimiter)
Znak nebo řetězec použitý k označení místa, kde má být jiný řetězec rozdělen.
set
Set je neuspořádaná kolekce nestejných prvků s neměnitelnou hodnou. Set samotný je měnitelný, viz Kap. 10.5.

8.19 Cvičení

  1. Napište smyčku, která traverzuje seznamem:
    ['spam!', 1, ['Brie', 'Roquefort', 'Pol le Veq'], [1, 2, 3]]
    
    a vytiskne délku každé položky. Co se stane, zadáme-li funkci len jako argument celé číslo?
  2. Vytvořte soubor ch09e02.py s následujícím obsahem:
    # Zde zadejte svoje doctesty:
    """
    """
    # Zde napiš svoje programy:
    
    if __name__ == '__main__':
        import doctest
        doctest.testmod()   
    
  3. Přidávejte následující doctesty do dokumentačního řetězce v předchozím skriptu a napište k nim vyhovující kódy.
    1. """
        >>> a_list[3]
        42
        >>> a_list[6]
        'Ni!'
        >>> len(a_list)
        8
      """
      
    2. """
        >>> b_list[1:]
        ['Stills', 'Nash']
        >>> group = b_list + c_list
        >>> group[-1]
        'Young'
      """
      
    3. """
        >>> 'war' in mystery_list
        False
        >>> 'peace' in mystery_list
        True
        >>> 'justice' in mystery_list
        True
        >>> 'oppression' in mystery_list
        False
        >>> 'equality' in mystery_list
        True
      """
      
    4. """
        >>> range(a, b, c)
        [5,9,13,17]
      """
      

    Přidávejte vždy jen jednu sadu doctestů a nepřidávej další, dokud předchozí sada neprošla zkouškami.

  4. Jaká je odezva IPP na následující zápis?
    >>> range(10, 0, -2)
    

    Tři argumenty pro funkci range jsou start, stop, step. V našem příkladě je start větší než stop. Co se stane, když start < stop a step < 0? Napiš pravidlo pro vztahy mezi start, stop a step.

  5. Nakreslete schematické zobrazení vztahu mezi a, b před provedením třetího řádku a po jeho provedení.
    a = [1, 2, 3]
    b = a[:]
    b[0] = 5
    
  6. Jaký bude výstup následujícího programu?
    this = ['I', 'am', 'not', 'a', 'crook']
    that = ['I', 'am', 'not', 'a', 'crook']
    print("Test 1: %s" % (id(this) == id(that)))
    that = this
    print("Test 2: %s" % (id(this) == id(that)))
    
    Přidejte podrobné vysvětlení výsledků.
  7. Vytvořte soubor ch09e06.py a použijte stejný postup jako ve cvičení 2 pro zajištění shody s doctesty:
    1. """
        >>> 13 in junk
        True
        >>> del junk[4]
        >>> junk
        [3, 7, 9, 10, 17, 21, 24, 27]
        >>> del junk[a:b]
        junk
        [3, 7, 27]
      """
      
    2. """
        >>> nlist[2][1]
        0
        >>> nlist[0][2]
        17
        >>> nlist[1][1]
        5
      """
      
    3. """
        >>> import string
        >>> string.split(message, '??')
        ['this', 'and', 'that']
      """
      
  8. Napište funkci add_lists(a,b) která přijme dva seznamy stejné délky s celými čísly a vrátí nový seznam se součty odpovídajících položek.
    def add_lists(a, b):
        """
        >>> add_lists([1,1], [1,1])     
        [2, 2]
        >>> add_lists([1,2], [1,4])     
        [2, 6]
        >>> add_lists([1,2,1], [1,4,3])     
        [2, 6, 4]
        """
    

    Fce add_lists musí být ve shodě s uvedenými doctesty.

  9. Napište funkci mult_lists(a, b) která přijme dva seznamy stejné délky s celými čísly a vrátí součet součinů odpovídajících položek.
    def mult_lists(a, b):
        """
        >>> mult_lists([1,1], [1,1])     
        2
        >>> mult_lists([1,2], [1,4])     
        9
        >>> mult_lists([1,2,1], [1,4,3])     
        12
        """
    

    Ověřte si, že fce mult_lists vyhovuje uvedeným doctestům.

  10. Přidejte následující dvě funkce do soubouru ch09_matrices.py, který jsme vytvořili v odstavci 9.19 (TDD).
    def add_row (matrix): 
        """
        >>> m = [[0,0], [0,0]] 
        >>> add_row(m)    
        [[0, 0], [0, 0], [0, 0]]
        >>> n = [[3, 2, 5], [1, 4, 7]]
        >>> add_row(n)
        [[3,2,5], [1,4,7], [0,0,0]]
        >>> n
        [[3,2,5], [1,4,7]]
        """
    def add_column (matrix): 
        """
        >>> m = [[0,0], [0,0]] 
        >>> add_column(m)    
        [[0,0,0], [0,0,0]
        >>> n = [[3,2], [5,1], [4,7]]
        >>> add_column(n)
        [[3,2,0], [5,1,0], [4,7,0]]
        >>> n
        [[3,2], [5,1], [4,7]]
        """
    

    Nové funkce musí projít zkouškou doctestů. Všimněte si, že poslední doctesty v každé funkci ověřují, že add_row a add_column jsou funkce čisté.

  11. Napište funkci add_matrices(m1, m2), která vrátí novou matici, jež je součtem matic m1 a m2. Sečíst dvě matice znamená sečíst hodnoty odpovídajících členů. Můžete předpokládat, že matice m1 a m2 mají stejnou velikost.
    def add_matrices (m1, m2): 
        """
        >>> a = [[1,2], [3,4]]
        >>> b = [[2,2], [2,2]] 
        >>> add_matrices(a, b)    
        [[3,4], [5,6]
        >>> c = [[8,2], [3,4], [5,7]]
        >>> d = [[3,2], [9,2], [10,12]]
        >>> add_matrices(c, d)
        [[11,4], [12,6], [15,19]]
        >>> c
        [[8,2], [3,4], [5,7]]
        >>> d
        [[3,2], [9,2], [10,12]]
        """
    
    Přidejte své funkce do ch09_matrices.py a ujistěte se, že projdou zkouškou uvedených doctestů. Poslední dva doctesty opět potvrzují, že add_matrices je čistá funkce.
  12. Napište funkci scalar_mult(n, m), která násobí matici m skalárem n. Přidejte svoji novou funkci do ch09_matrices.py a ujistěte se, že projde doctesty.
    def scalar_mult (n, m): 
        """
        >>> a = [[1,2], [3,4]]
        >>> scalar_mult(3, a)    
        [[3,6], [9,12]
        >>> b = [[3,5,7], [1,1,1], [0,2,0], [2,2,3]]
        >>> scalar_mult(10, b)
        [[30,50,70], [10,10,10], [0,20,0], [20,20,30]]
        >>> b
        [[3,5,7], [1,1,1], [0,2,0], [2,2,3]]
        """
    
  13. Přidejte nové funkce do ch09_matrices.py a zajistěte shodu s doctesty.
    def row_times_column (m1, row, m2, column):     """     >>> row_times_column[[1,2], [3,4], 0, [5,6], [7,8], 0]     19     >>> row_times_column[[1,2], [3,4], 0, [5,6], [7,8], 1]     22     >>> row_times_column[[1,2], [3,4], 1, [5,6], [7,8], 0]     43     >>> row_times_column[[1,2], [3,4], 1, [5,6], [7,8], 1]     50     """
    def matrix_mult (m1, m2):     """     >>> matrix_mult([[1,2], [3,4]], [[5,6], [7,8]])     [[19,22], [43,50]]     >>> matrix_mult([[1,2,3], [4,5,6]], [[7,8], [9,1], [2,3]])     [[31,19], [85,55]]     >>> matrix_mult([[7,8], [9,1], [2,3]], [[1,2,3], [4,5,6]])     [[39,54,69], [13,23,33], [14,19,24]]     """
  14. Vyzkoušejte si různé separátory u funkcí join a split.
  15. Napište funkci replace(s, old, new), která zamění všechny výskyty old za new v řetězci s.
    def replace (s, old, new): 
        """
        >>> replace ('Mississippi','i', 'I')
        'MIssIssIppI'
        >>> s = 'I love spom! Spom is my favorite food. Spom, spom, spom, yum!'
        >>> replace(s, 'om', 'am')
        'I love spam! Spam is my favorite food. Spam, spam, spam, yum!'
        >>> replace(s, 'o', 'a')
        'I lave spam! Spam is my favarite faad. Spam, spam, spam, yum!'
        """
    
    Řešení musí zajité vyhovovat doctestům. Použije se split a join.
  16. Uveďte odezvy IPP na následující zápisy:
    1. >>> nums = [1, 2, 3, 4]
      >>> [x**3 for x in nums]
      
    2. >>> nums = [1, 2, 3, 4]
      >>> [x**2 for x in nums if x**2 != 4]
      
    3. >>> nums = [1, 2, 3, 4]
      >>> [(x, y) for x in nums for y in nums]
      
    4. >>> nums = [1, 2, 3, 4]
      >>> [(x, y) for x in nums for y in nums if x != y]
      
    Měl byste předjímat výsledky předtím, než vám je ukáže IPP.

previous up next hi end end