Iterace
je opakovaný výpočet pro hodnoty jednotlivých členů
Iterace iteráblu produkuje 'dychtivý' (eager) výstup, iterace iterátoru produkuje 'líný' (lazy) výstup, viz odst. 3.6.3.
Iterábl
je
Iterátor
je interní objekt, vlastnící vestavěnou funkci
Implicitně si iterátor pro zadaný iterábl vytváří smyčka
Iterátory jsou výhodné tím, že snižují spotřebu paměti; vyprázdněný iterátor je "garbage collected".
Iterátor lze generovat i generátorovou funkcí a generátorovým výrazem - viz odst. 3.6.
Iterátory, definované třídou, jsou popsány v Kap. 10.7.
Funkce
>>> li = [2, 5, 0]# iterable >>> ili = iter(li)# vytvoření iterátoru >>> ili = li.__iter__() # alternativní způsob >>> next(ili)# první krok iterace 2 >>> ili.__next__()# alternativně další krok iterace 5 >>> next(ili)# poslední krok iterace 0 >>> next(ili)StopIteration # info o konci iterace
Iterátor je v této chvíli prázdný. Opětovně použitelný iterátor vytvoříme opětovným použitím funkce
To platí i při aplikaci smyčky
>>> ili = iter(li) >>> next(ili) 2 >>> for i in ili: print(i, end =" ") 5 0 >>> next(ili)StopIteration Nutno poznamenat, že při aplikaci smyčky for .. pro iterábl li žádné " >>> for i in li: print(i, end =", ") 2, 5, 0,StopIteration " nehrozí, neboť si smyčka vytvoří svůj vlastní interní iterátor:
Elementy sekvencí jsou interně
Názornou ilustraci indexování těchto elementů představuje obrázek:
Indexování zleva začíná nulou, indexování zprava doleva se provádní pomocí záporných čísel.:
>>> pozdrav = "Hello World" >>> pozdrav[5] ' ' >>> pozdrav[-5] 'W' >>> pozdrav[11]IndexError: string index out of range >>> pozdrav.__getitem__(0) 'H'
Hodnoty elementů objektu typu
>>> dc = {"m": 5, "n": 6}# objekt s klíči "m", "n" >>> dc["m"]# použití závorkového operátoru 5 >>> dc.__getitem__("n")# použití metody 6 >>> for i in dc:# použití indexu ve smyčce print(dc[i], end =" ") 5 6
Vymezená část sekvence typu
Skladba příkazu:
Není-li
V následující tabulce jsou u všech příkazů zobrazeny orientace deklarovaných úseků. U některých příkazů jsou explicitně rozepsány hodnoty explicitních i implicitních parametrů:
# 0 1 2 3 4 5 6 indexy 0 ÷ 6 seq = [1, 2, 3, 4, 5, 6, 7]upravovaná sekvence # -7 -6 -5 -4 -3 -2 -1 indexy -1 ÷ -7 >>> a = seq[::]; a# --> [1, 2, 3, 4, 5, 6, 7] # dtto seq[:] # 0 : (len(seq)+1) : 1 >>> b = seq[2::]; b# --> [3, 4, 5, 6, 7] # dtto seq[2:] # 2 : (len(seq)+1) : 1 >>> c = seq[:5:]; c# --> [1, 2, 3, 4, 5] # dtto seq[:5] # 0 : 5 : 1 >>> d = seq[2:6:]; d# --> [3, 4, 5, 6] # dtto seq[2:6] # 2 : 6 : 1 >>> e = seq[::2]; e# --> [1, 3, 5, 7] # 0 : (len(seq)+1) : 2 >>> f = seq[::-2]; f# <-- [7, 5, 3, 1] >>> g = seq[2:6:2]; g# --> [3, 5] >>> h = seq[2:6:-2]; h# --> [] (prázdná subsekvence) >>> i = seq[6:2:2]; i# <-- [] (prázdná subsekvence) >>> j = seq[2::2]; j# --> [3, 5, 7] # 2 : (len(seq)+1) : 2 >>> k = seq[8:1:-2]; k# <-- [7, 5, 3] >>> l = seq[-4:-2]; l# --> [4, 5] # -4 : -2 : 1
Poznámka:
Úsek je jako orientovaná úsečka (
Krok vychází z počátku úseku. Pokud mají úsek a krok opačnou orientaci, je výsledkem prázdný úsek - viz případ
Skladba parametrů funkce
Podle znaménka indexů start, end může být úsek orientován zleva doprava nebo zprava doleva. Stejně tak krok může směřovat vpravo či vlevo - opět v závislosti na kladné či záporné hodnotě kroku.
Působení funkce slice() je zajímavé. Tato funkce vytvoří objekt bez ohledu na zamýšlenou sekvenci:
seq = "Nazdar Pythone!" slice_obj = slice(3,11)# opakovaně použitelný objekt # Tento objekt lze uplatnit na sekvence různých typů: sub_seq = seq[slice_obj] print(sub_seq)# --> dar Pyth # Případně lze pro jedno použití slice(3,11) dosadit přímo: sub_seq = seq[slice(3,11)] print(sub_seq)# --> dar Pyth
Lepší představě o vlivu znamének na vytvoření úseku může posloužit toto schéma indexů pro použitý řetězec:
# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # seq: "N a z d a r P y t h o n e !" # -15 -14 -13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
V následující tabulce jsou u všech příkazů zobrazeny orientace deklarovaných úseků. U některých příkazů jsou explicitně rozepsány hodnoty explicitních i implicitních parametrů:
>>> seq = "Nazdar Pythone!" >>> ap = seq[slice(3)]; ap# --> Naz # 0 : 3 : 1 >>> an = seq[slice(-3)]; an# --> Nazdar Pytho # 0 : -3 : 1 >>> b1p = seq[slice(3,5)]; b1p# --> da # 3 : 5 : 1 >>> b1n = seq[slice(-3,-5)]; b1n# <-- prázdná subsekv. >>> b2p = seq[slice(5,3)]; b2p# <-- prázdná subsekv. >>> b2n = seq[slice(-5,-3)]; b2n# --> ho subsekv. >>> b3i = seq[slice(3,-5)]; 3i# --> dar Pyt >>> b3j = seq[slice(-3,5)]; b3j# <-- prázdná subsekv. >>> cp = seq[slice(3,12,2)]; cp# --> drPto >>> cn = seq[slice(-3,-12,-2)]; cn# <-- nt >>> cm = seq[slice(3,-12,-2)]; cm# <-- prázdná subsekv. >>> d = seq[slice(-2, -6)]; d# <-- prázdná subsekv. >>> e = seq[slice(-6, -2)]; e# --> thon >>> f = seq[slice(-2, -6, -1)]; f# <-- enoh >>> g = seq[slice(-6, -2, -1)]; g# --> prázdná subsekv.
Dobrou zvláštní okolností při zadávání kladně i záporně orientovného úseku je skutečnost, že jeho deklarovaná délka může být delší než
>>> seq_op = [1, 2, 3, 4, 5, 6, 7] >>> seq_sl = "Nazdar Pythone!" d = seq_op[2:9]; d# --> [3, 4, 5, 6, 7] f = seq_sl[slice(-10, -20, -1)]; f# <-- radzaN
Hromadné přiřazení (multiple assignment) umožňuje přiřadit jediným příkazem výčet hodnot na pravé straně k výčtu jmen na straně levé. Tento příkaz lze zadat dvojím způsobem:
# V jednom řádku: >>> x,y,z = 10,20,30; x,y,z# 3 jména <-- 3 hodnoty (10, 20, 30)# Ve dvou řádcích: >>> seq = 1,2,3# 1 jméno <-- 3 hodnoty >>> a,b,c = seq# 3 jména <-- 1 entice >>> a*b*c 6# Při přiřazení došlo k rozbalení hodnot z entice.
Při stejném počtu prvků na obou stranách proběhne jejich propojení v zadaném pořadí.
Výčet hodnot na pravé straně může mít formát entice (tuple), seznamu (list), řetězce (string), setu (set), slovníku (dictionary) a rozsahu (range).
>>> a,b = 12, 22; a,b# tuple = tuple (12, 22) >>> a,b = [12, 22]; a,b# tuple = list (12, 22) >>> c,d,e = "Haf"; c,d,e# tuple = string ('H', 'a', 'f') >>> f,g = {5, "pivo", 5}; f,g# tuple = set ('pivo', 5) >>> f,g = {"j":15, "k":25}; f,g# tuple = dict ('j', 'k')(vrací jen klíče) >>> b,f,l = range(3); b,f,l# tuple = range (0, 1, 2)
Hromadným přiřazením rozbalíme slovník ve smyčce for ...
>>> dic = {'a':2, 'b':4, 'c':10}# deklarace slovníku >>> for pol in dic .items() :# vestavěná methoda key, value = pol# hromadné přiřazení print(f"Klíč {key} má hodnotu {value}") Klíč a má hodnotu 2 Klíč b má hodnotu 4 Klíč c má hodnotu 10
Hromadné přiřazení lze výhodně použít i ve spojení s funkcí, pokud její invokace produkuje stejný počet hodnot jako zadaný počet proměnných:
>>> def mocniny(num): return num, num**2, num**3# fce vrací 3 hodnoty >>> num, sqr, cub = mocniny(2)# jejich přiřazení >>> num, sqr, cub# invokace (2, 4, 8)
Hromadné přiřazení použijeme i ve spojení s komprehencí iteráblu (viz 3.5):
>>> x,y,z = (x**2 for x in (1,3,5))# hromadné přiřazení >>> x,y,z# invokace (1, 9, 25)
Hromadným přiřazením lze rozbalit i vnořené iterábly:
>>> body = (1,2), (-1,-2)# entice entic >>> (x1,y1), (x2,y2) = body# hromadné přiřazení >>> print(f"x1,y1 ={x1,y1}, x2,y2 ={x2,y2}") x1,y1 =(1, 2), x2,y2 =(-1, -2)
Hromadné přířazení použijeme výhodně při záměně přiřazení:
>>> x,y,z = 10,20,30 >>> z,y,x = x,y,z >>> x,y,z (30, 20, 10)
Alternativní označení hromadného přiřazení je
Pokud je
>>> *a, = 1,2,3,4,5; a# *a, je entice (tuple) [1,2,3,4,5]# tuple 'rozbaluje' do listu >>> [*a] = 1,2,3,4,5; a [1,2,3,4,5]# stejně jako list
Ve výčtu jmen na levé straně smí být jen jedna hvězdičková proměnná. Neohvězdičkovaná jména se nazývají
>>> a,*b,c = 1,2,3,4,5; a,b,c (1,[2,3,4],5) >>> a,*_,c = 1,2,3,4,5; a,_,c# _ je 'anonymní' proměnná (1,[2,3,4],5) >>> a,*b,c = 1,2; a,b,c# OK: 3 <= 1 + 2 (1,[],2)# hodnoty 1,2 měly přednost, na b* nic nezbylo
Možné jsou i tyto (poněkud rozpustilé) kombinace:
>>> f,*m,(*ir,il) = 0,1,2,3,(4,5,6)# přiřazení >>> f; m; ir; il# invokace 0 [1, 2, 3] [4, 5] 6
Hvězdičkové operátory jsou mnohdy nezbytné pro rozbalení některých iteráblů:
>>> *ran, = range(8); ran# tuple či list na levé straně! [0, 1, 2, 3, 4, 5, 6, 7] >>> r = list(ran); r# alternativní invokace [0, 1, 2, 3, 4, 5, 6, 7] >>> *gen, = [2**x for x in range(8)]; gen# komprehence [1, 2, 4, 8, 16, 32, 64, 128]
Hromadné přiřazení s hvězdičkovým členem usnadňuje přístup k prvkům iteráblu bez použití indexů:
>>> items = "zdař", 25, True, 2+5Výběr pomocí indexů: >>> print(f"První položka je {items[0]}, poslední je {items[-1]}") První položka je zdař, poslední je 7Výběr hromadným přiřazením: >>> first, *middle, last = items >>> print(f"Prostřední položka je {middle}, poslední je {last}") Prostřední položka je [25, True], poslední je 7
Dekorace **, uvedená před názvem slovníku, přikazuje jeho rozbalení před dalším použitím:
>>> dic = {'a':2, 'b':4, 'c':10} >>> def fun(a,b,c): return a,b,c >>> fun(**dic) (2, 4, 10)
Jiný příklad se sloučením slovníků:
>>> prvý = {"A": 1, "B": 2} >>> druhý = {"C": 3, "D": 4} >>> spolu = {**prvý,**druhý,**{"e": 5, "f": 6}}# deklarace >>> spolu# invokace {'A': 1, 'B': 2, 'C': 3, 'D': 4, 'e': 5, 'f': 6}
Jen tak mimochodem - stejně úsporně lze totéž provést operátorem
>>> prvý = {"A": 1, "B": 2} >>> druhý = {"C": 3, "D": 4} >>> spolu = prvý | druhý | {"e": 5, "f": 6} >>> spolu {'A': 1, 'B': 2, 'C': 3, 'D': 4, 'e': 5, 'f': 6}
Komprehence je skladebný předpis pro vytvoření
Komprehenci ad b) lze ilustrovat matematickým výrazem, který pro všechna
S = { 2*x | x € N, x² > 3 }
Obdobou výše uvedeného matematického výrazu může být toto schéma skladby v Pythonu:
S =< f(x) for x in <objekt> # bez podmínky S =< f(x) for x in <objekt> if <podmínka>>
Závorky kolem deklarované komprehence jsou povinné a mají vliv na výsledný typ modifikovaného objektu. Používají se závorky:
Idiom (
Ukažme si konkretní příklad komprehence bez podmínky u
Nutno poznamenat, že pro řádné provedení transformací je nutné aby objekty typu set a klíče slovníku neodkazovaly na typ string:
# Výpis výchozích iteráblů: li, tu = [1, 2, 3, 4], (1, 2, 3, 4)# list, tuple rg, sg = range(2,6), "růže"# range, string st = {4, 7, 4, True}# set fs = frozenset(st)# frozenset dc = {4:"size", 5:"Karel"}# dictionary by = bytes(sg, 'utf-8')# bytes from string ba, mv = bytearray(by), memoryview(by)# bytearray, memoryview # Použité závorky ( >>><> =[], (), {} ) mají vliv na výsledný typ modifikovaného objektu. Pokusíme-li se o obecný záznam zamýšlené komprehence, mohli bychom napsat třeba toto:mod_obj =< x*2 for x initerable > # kde <> =[], {} # Kulaté závorky >>>() použijeme s funkcítuple :mod_obj = tuple(x*2 for x initerable )# Bez funkce >>>tuple získáme místo upraveného objektu generátorový objekt, například:gen_obj = (x*2 for x inmv ) <generator object <genexpr> at 0x00000124DACB2810>
Jak uvedeno, iterábly lze komprehovat pro výstup ve formátu
Dále si ukážeme komprehenci s podmínkou:
>>> numbers = [1, 3, 2, 5, 3, 1, 4] >>> {x**2 for x inVýrazových předpisů může být v jedné komprehenci více:numbers if x**2 > 3} {16, 9, 4, 25}# komprehence s podmínkou a redukcí >>> >>> files = ('bin', 'Data', 'Desktop', '.bashrc', '.ssh', '.vimrc') >>> [name for name in files if name[0] != '.'] ['bin', 'Data', 'Desktop']# komprehence s podmínkou
>>> [(x, x**2, x**3) for x inMůže být více i poskytnutých objektů:numbers ]# komprehence seznamu [(1, 1, 1), (2, 4, 8), (3, 9, 27), (4, 16, 64)]
>>> numbers = (1, 2, 3, 4) >>> letters = ['a', 'b', 'c'] >>> [n*letter for n in numbers for letter in letters] ['a', 'b', 'c', 'aa', 'clr-bb', 'cc', 'aaa', 'clr-bbb', 'ccc', 'aaaa', 'clr-bbclr-bb', 'cccc']
Zajímavá je úprava argumentu v této komprehenci:
>>> [v*2 for v in ("a", 5, True)] ['aa', 10, 2]# 2*True = 2*1 = 2
Za povšimnutí stojí i toto spojení komprehence s formátováním řetězce dle kap. 6.11:
>>> chlapci = ["Pavel", "Petr", "Jan"] >>> synci = ["{} Novotný".format(boy) for boy in chlapci] >>> synci ['Pavel Novotný', 'Petr Novotný', 'Jan Novotný']
Ukázka komprehence setu z řetězce:
>>> s = {v for v in "ABCDABCD" if v not in "CB"}; s {'A', 'D'}
Komprehencí setu z řetězce lze s pomocí funkce
>>> d = {key:val for key,val in enumerate ('ABCD') if val not in "CB"} >>> d {0: 'A', 3: 'D'}
Zajímavé je použití pojmenovaného výrazu (mroží operátor - 2.6.2) při komprehenci seznamu:
>>> def f(x): return x + 2 >>> print([(y := f(x), x/y) for x in range(3)]) [[2, 0.0], [3, 0.3333333333333333], [4, 0.5]]případně se zaokrouhlením des. čísla dle kap. 6.13: >>> print([[y := f(x), round(x/y, 2)] for x in range(3)]) [[2, 0.0], [3, 0.33], [4, 0.5]]případně s podmínkou a přehledněji: >>> print([(x, y, round(x/y, 2)) for x in range(3) if (y := f(x)) > 0]) [(0, 2, 0.0), (1, 3, 0.33), (2, 4, 0.5)]
Následující komprehencí vybereme ze zadané množiny bodů ty, které leží uvnitř kružnice o poloměru r = 2:
>>> import math >>> radius = 2 >>> [(x, y) for x in range(-2,3) for y in range(-2,3) if ... math.sqrt(x**2 + y**2) < radius] [(-1,-1), (-1,0), (-1,1), (0,-1), (0,0), (0,1), (1,-1), (1,0), (1,1)]
Generátor (
Tento objekt je iterátorem, který neobsahuje žádná data, pouze je evokuje v okamžiku invokace.
Generátorový výraz má formát idiomu
Iterace
V následující ukázce jsou obě iterace realizovány smyčkou
>>> st = "abakus"# iterovatelný objekt - iteráble >>> stv =(i for i in st) # generátorový objekt - iterátor >>> type(stv) <class 'generator'> >>> for i instv : print (i,end=" ")# líná (lazy) iterace iterátoru a b a k u s >>> for i inst : print (i,end=" ")# dychtivá (eager) iterace iteráblu a b a k u s
Generátorový výraz lze vytvořit pro traverzování všech iteráblů. Zde vytvoříme iterátor pro iterábl typu range():
>>> gv = (i*i for i in range(5))# iterátor >>> for i in gv: print (i, sep=", ")# líná (lazy) iterace 0, 1, 4, 9, 16
Generátorová funkce je normální funkce, která místo příkazu
def count_to(m) :# vrací iterátor n = 0while n <= m:yield n # poskytuje elementy iterátoru n += 1
>>> ct = count_to(5)# gener. objekt, iterátor # Invokace iterátoru: >>> next(ct)# 1. krok iterace (funkce) 0 >>> ct.__next__()# 2. krok iterace (metoda) 1 >>>for i in ct: print (i, end=" ")# pokračování iterace 2 3 4 5smyčkou for ... >>> next(ct)StopIteration # iterátor je prázdný >>> ct = count_to(5)# objekt nutno obnovit >>> print(list(ct)) [0, 1, 2, 3, 4, 5]# výtisk generátor. objektu >>> print(list(ct)) []# iterátor je prázdný
Generátorový objekt (iterátor) vytvoří i tato generátorová funkce:
def squares (start, stop):for iin range (start, stop):yield i * i
Přiřazením invokace generátorové funkce ke jménu vytvoříme iterovatelný
Iteraci provedeme buď opakovaným použitím fce
>>> sqr = squares(1,7)# lazy iterátor >>> next(sqr), next(sqr)# opakovaná iterace (1, 4) >>> for i in sqr:# smyčkou for ... vyčerpáme print (i, end=" ")zbytek najednou 9 16 25 36 >>> sqr = squares(1,7)# obnova iterátoru >>> list(sqr)# najednou fcí list() [1 4 9 16 25 36]
Výpočet řady Fibonacciho čísel můžeme provést pomocí následující generátorové funkce s nekonečnou smyčkou:
def fibo ():# generátorová funkce > a, b = 0, 1while True:# nekonečná smyčka yield a a, b = b, a+b
Generátorový objekt (iterátor) s nekonečnou smyčkou voláme buď po krocích nebo pro konkretní mez, určenou funkcí
>> f = fibo()# generátorový objekt, iterátor >> f.__next__()# --> 0 jiná verze fce next() >> next(f), next(f)# --> (1, 1) # Můžeme pokračovat do omrzení pomocí fce range(): >>> for i in range(4): print(next(f), sep=", ")# --> 2, 3, 5, 8 >>> for i in range(5): print(next(f), sep=", ")# --> 13, 21, 34, 55, 89
Funkce
Generátorový výraz a generátorová funkce vrací generátorový objekt, jímž je pomalu (lazy) vyhodnocovaný iterátor.
Při líném výpočtu (lazy evaluation) se odkládá výpočet výrazu až do chvíle jeho potřeby. Důsledkem je zmenšená potřeba zdrojů (paměti) při zvýšeném výkonu procedury.
Lze sestavit potenciálně nekonečné struktury, např. smyčky - viz fibonacci().
Líný výpočet se obtížně kombinuje s imperativními strukturami jako je ošetření výjímek nebo vstup/výstup; může vytvářet "space leaks" (viz).
Příkladem procedury, uplatňující líný výpočet, je použití slovníku v kapitole 9.12 (Switch case), kde u funkce
Všechny aktivity spuštěného programu se odvíjejí od textu, vyskytujícího se v různých lexikálních (textových) oblastech kódu, zvaných jmenný prostor (namespace), JP.
Těmito oblastmi (jmennými prostory) jsou:
Poznámka: O funkcích a třídách víme, že jejich definice se skládají ze záhlaví a z těla (funkce či třídy). Jejich lokální oblast je tvořena jejch vnitřním prostorem (tělem).
Jmennými oblastmi jsou tedy tyto oblasti (anglicky):
Objekty jsou přístupné prostřednictvím jmen, které na ně odkazují. Vázání objektu ke jménu je realizováno:
mamoj = "František"
def fun(m)# záhlaví print(m*m)# tělo
Jméno je na předním místě v pořadí důležitosti entit. Rozdělení zdrojového kódu do výše uvedených oblastí (
V jedné oblasti se nemohou současně vyskytovat dvě stejná jména, odkazující na různé hodnoty:
mam = "Alena"# deklarace přiřazení mam = "Petr"# změna přiřazení, předchozí deklarace neplatí
Mohou se však bez kolize vyskytovat současně v různých oblastech - v různých jmenných prostorech.
Globální proměnná je tvořená v globální oblasti skriptu a je přímo dostupná z globální i z lokální oblasti (funkce) - interpret hledá jméno tam, kde je žádané, případně pokračuje v hierarchicky nadřazené oblasti.
glob_lok.py def fun():# lokální prostor funkce v glob. prostoru skriptu: s = "Toto je lokální 's'"# lokální proměnná print(s)
# globální prostor interpreta Pythonu: >>> s = "Toto je globální 's'"# globální proměnná >>> fun()# invokace funkce fun() Toto je lokální 's' >>> print(s)# invokace funkce print() Toto je globální 's'
Vidíme, že jedinečnost jména je dána jeho umístěním. Příkaz print(s) v globálním prostoru ignoruje přiřazení jména v lokálním prostoru, neboť toto jméno je mu přímo nedostuné.
Přítomnost jména ve jmenném prostoru je zapsána ve jmenném registru, jenž má formu slovníku (
# global.py # globální prostor skriptu a = [1, 2, 3, 4, 5]# globální proměnná skriptu def foo():# globální objekt foo() b = 11# lokální proměnná funkce print(locals ())
>>> foo() {'b': 11}# registr lokálního prostoru foo() >>> print(globals ())# registr globálního prostoru skriptu 'global.py': {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__':none, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'F:\\Codetest\\python\\Ukázky\\scopes\\glob_loc.py', 'a': [1, 2, 3, 4, 5], 'foo': <function foo at 0x0000023E6039CFE0>}
Všimněme si, že se proměnná
Zavedené označení výše popsaného slovníku je
Jmennými oblastmi jsou oblasti (anglicky):
Souhrn lexikálních oblastí, z nichž je jméno přímo dostupné, se nazývá scope tohoto jména. Do tohoto souhrnu patří jmenný prostor, v němž je jméno deklarováno a všechny níže postavené jmenné prostory. Obecně lze řící, že z výše postavené oblasti nelze volat jméno v níže postavené oblasti.
Dostupnost jmen si ukážeme na příkladu funkce
Následující soubor
# glob_local.py # globální prostor glo = "Živijó"# globální proměnná def fun_lok ():# globální proměnná lok = "lokální"# lokální proměnná # built_in def fun_inn ():# lokální proměnná inn = "inner"# vnitřní proměnná locals ())# built_in fun_inn()# invokace vnořené funkce
>>> fun_lok()# invokace glob. fce glo, lok = Živijó, lokální# výstup lok. fce 'print()' glo, lok, inn = Živijó, lokální, inner# výstup 1. vnitřní fce 'print()' {'inn': 'inner', 'lok': 'lokální'}# výstup 2. vnitřní fce 'print()'; lokální JR
Na příkladu tedy vidíme, že:
Proměnná
>>> __name__# --> '__main__' - invokace ad a) >>> import my_file >>> my_file.__name__# --> 'my_file' - invokace ad b)
Případ si ilustrujme na podrobnější ukázce. Mějme dva soubory, prvnímu budeme říkat modul, protože jej chceme importovat do jiného souboru:
# cosi_name.py def cosi(): print('Hodnota proměnné __name__ jest: ', __name__) if __name__ == "cosi_name": print("Nekapišto.")
Provedením modulu v Thonny zjistíme, že je ticho po pěšině - deklarovaná funkce nebyla volána, idiom "if __name__ == 'cosi_name' " je nepravdivý, tudíž se příkaz print('Nekapišto.') nerealizuje:
>>>%Run cosi_name.py >>>
Napíšeme si další soubor ...
# other_name.py import cosi_name# import modulu print('Pro cosi_name je __name__ =', cosi_name.__name__) print('Pro other_name je __name__ =', __name__) if __name__ == "cosi_name": print("Kapišto?")
... a po jeho spuštění zjistíme, že nám na prvním místě figuruje výstup z importovaného souboru cosi_name.py:
>>>%Run other_name.py Nekapišto.# naše záhada Pro cosi_name je __name__ = cosi_name# jasná páka Pro other_name je __name__ = __main__# jasná páka
Importovaný soubor cosi_name.py změnil status. Jeho atribut __name__ má nyní hodnotu '__cosi_name__' - proto mohlo dojít k vytištění řetězce 'Nekapišto.' a to ještě před výstupy ze souboru other_name.py.
Introspekce Pythonu je akt zkoumání obsahu a vlastností jednotlivých jeho objektů. Za tímto účelem disponuje Python řadou vestavěných funkcí, z nichž některé již známe.
>>> omen = "Alenka" >>> dir(omen) ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', ...
>>> isinstance(5, int) True
>>> getattr(omen, "__doc__") "str(object='') -> str\nstr(bytes_or_buffer[, ... ... sys.getdefaultencoding().\nerrors defaults to 'strict'."
Dalším introspekčním nástrojem je t.zv.
docstring (dokumentační řetězec), což je krátká informace ve formátu řetězce (
Při rozvoji programu se s oblibou provádí testování vybraných úseků zdrojového kódu. Pro toto testování poskytuje Python moduly doctest a unittest.
Popíšeme si práci s modulem
Modul
def is_divisible_by_2_or_5 (n):""" >>> is_divisible_by_2_or_5(8) True >>> is_divisible_by_2_or_5(7) False >>> is_divisible_by_2_or_5(5) True """ return n % 2 == 0or n % 5 == 0if __name__ == '__main__':import doctest doctest.testmod()
Poslední tři řádky spouští celou parádu. Umisťujeme je na konec každého souboru, který obsahuje doctesty.
Pokud procedura zjistí shodu mezi zkoumanými vzorky a zadanými výsledky, reaguje "mlčením" nebo je-li v konzole evokována funkce "doctest.testmod()", vrátí stručný komentář o průběhu
>>> doctest.testmod()TestResults(failed=0, aclr-ttempted=3)
Pokud narazí na rozpor, spustí rozsáhlé chybové hlášení.
Spuštění neúspěšného skriptu vyprodukuje například následující výstup:
Failed example: is_divisible_by_2_or_5(7) Expected: True Got: False ***********************************************************1 items had failures: 1 of 3 in __main__.is_divisible_by_2_or_5 ***Test Failed*** 1 failures.
stvoří tento výstup
1 1 2 3 3 6 4 10 5 15
if __name__ == '__main__': import doctest doctest.testmod()
Funkce
def num_digits (n): """ >>> num_digits(12345) 5 >>> num_digits(0) 1 >>> num_digits(-12345) 5 """
def num_even_digits (n): """ >>> num_even_digits(123456) 3 >>> num_even_digits(2468) 4 >>> num_even_digits(1357) 0 >>> num_even_digits(2) 1 >>> num_even_digits(20) 2 """
Nutno zajistit, aby mezi číslicemi výstupu byly mezery a aby za poslední číslicí žádná mezera nebyla. Numerický argument funkce nutno před manipulací přetvořit nadef print_digits (n): """ >>> print_digits(13789) 9 8 7 3 1 >>> print_digits(39874613) 3 1 6 4 7 8 9 3 >>> print_digits(213141) 1 4 1 3 1 2 """
Svá řešení ověřte pomocí doctestů.def sum_of_squares_of_digits (n): """ >>> sum_of_squares(1) 1 >>> sum_of_squares(9) 81 >>> sum_of_squares(11) 2 >>> sum_of_squares(121) 6 >>> sum_of_squares(987) 194 """