Hlavní navigace

Létající cirkus (4)

1. 2. 2002
Doba čtení: 9 minut

Sdílet

Dnešní díl bude věnovaný proměnným, prostorům jmen a uživatelským funkcím v jazyce Python. Ukážeme si konstrukce global, del a def. Povíme si o tom, jak si vytvořit vlastní uživatelskou funkci, jak používat nepovinné parametry, a na závěr si ukážeme, jak naši funkci zavolat z programu.

Proměnné a prostory jmen

Proměnné, jak je známe z jiných kompilovaných jazyků, v Pythonu nenajdeme. Zde je každá hodnota, funkce, třída, metoda, instance třídy nebo modul objektem v paměti interpretru a proměnná je jen odkazem na tento objekt. Správa paměti je obsluhovaná automatickým garbage collectorem, založeným na počítání odkazů. Díky tomu se v Pythonu vůbec nemusíme starat o přidělenou paměť. Každý objekt má počítadlo odkazů, které je při vytváření objektu vynulováno. Při vzniku odkazu na tento objekt (tedy nejen, přiřadíme-li objekt nějaké proměnné, ale vložíme-li ho třeba do seznamu apod.) je hodnota počítadla objektu zvýšena o 1, při zrušení odkazu je hodnota snížena o 1, a až pokud je hodnota tohoto počítadla rovna nule, dojde k fyzickému odstranění objektu z paměti (u instancí tříd také k zavolání destruktoru). V Pythonu nenajdeme žádné deklarace, novou proměnnou prostě vytvoříme, přiřadíme-li jí nějakou hodnotu. Použití neexistující proměnné ale vede k výjimce, a proto nedochází ke zmatkům jako např. v BASICu, kde jsme proměnné „pocet“ přiřadili hodnotu a dále jsme třeba počítali s neexistující proměnnou „poce“.

Mapování jmen proměnných na objekty mají na starosti prostory jmen. Ty jsou implementovány jako asociativní pole, jejichž klíčem je jméno proměnné a hodnotou je vlastní objekt, na který proměnná odkazuje. Python používá tři prostory jmen (namespace) – globální, lokální a builtin (česky snad „vestavěný“, ale já se budu držet anglického originálu).

  • Globální – zde jsou uloženy globální proměnné. Program má k těmto proměnným přístup jen pro čtení. Potřebujeme-li přiřadit globální proměnné nějakou hodnotu, musíme proměnnou deklarovat jako globální konstrukcí global (viz. následující odstavce). Neučiníme-li tak, dojde k vytvoření nové proměnné v lokálním prostoru jmen a hodnota globální proměnné změněna nebude!
  • Lokální – zde jsou uloženy lokální proměnné. Všechny nově vytvářené proměnné jsou umístěny v tomto prostoru jmen (nejsou-li však deklarovány jako globální konstrukcí global).
  • Builtin – všechny proměnné vestavěné v interpretru. Zaveden proto, aby se program na tyto proměnné mohl odkazovat přímo bez použití tečkové notace (blíže viz další díl, který bude věnován modulům a balíčkům)

Prostory jmen jsou prohledávány v pořadí lokální, globální, builtin. Význam lokálních a globálních prostorů jmen se mění v závislosti na aktuálním kontextu, tj. na místě, kde se zrovna program nachází. Např. v těle modulu je globální a lokální prostor jmen totožný, v těle funkce je globální prostor jmen stejný jako globální prostor jmen nadřízeného bloku, lokální prostor jmen se vytvoří vždy nový apod. Vše vám podrobně vysvětlí dokumentace jazyka Python.

Konstrukce global a del

Tyto dvě konstrukce těsně souvisí s proměnnými, ta první deklaruje proměnné jako globální, druhá odstraní proměnné z prostoru jmen. Jejich syntaxe je velice jednoduchá, za klíčovým slovem global nebo del následuje seznam proměnných oddělených čárkou, které chcete deklarovat jako globální, resp. odstranit:

>>> a = b = c = 10
>>> global a, c # a, c jsou globalni promenne
>>> del a, b    # odstranime promenne a, b

Deklarování globální proměnné platí od místa, kde tak bylo učiněno, do konce aktuálního bloku kódu. Blok kódu je: modul, tělo funkce, definice třídy, každý příkaz, který je zadán interaktivně, je také samostatný blok kódu, soubor s kódem a dále kód předaný konstrukci exec nebo funkcím eval(), execfile() a input(). Proměnné, která má být deklarována jako globální, nesmí být před konstrukcí global přiřazena žádná hodnota.

Definování uživatelských funkcí

Definováním funkce vytvoříme nový spustitelný objekt a nový záznam v lokálním prostoru jmen. Spustitelný funkční objekt obsahuje informace o svém vlastním kódu a hlavně o globálním prostoru jmen, který se použije, je-li jeho kód vykonáván. Definice funkce pouze vytvoří nový objekt, kód je vykonán až při jeho spuštění – zavolání funkce. Samozřejmostí je rekurze (maximální hloubka zanoření je omezena, její velikost zjišťujeme funkcí sys.getrecursi­onlimit() a nastavujeme funkcí sys.setrecursi­onlimit(), implicitně je nastavena na 1000 a překročení tohoto limitu vede k výjimce, ne k pádu interpretru), je také možné používat vnořené funkce. Ty ale nemají přístup k proměnným vnější funkce, dokumentace k jazyku se zmiňuje o možnosti obejít tento ‚nedostatek‘, který vyplývá z filozofie dvou prostorů jmen, použitím nepovinných parametrů a jejich výchozích hodnot. Nakolik je ale toto řešení elegantní, nechám na vašem zvážení.

Jméno funkce v programu vystupuje jako proměnná, které je přiřazen funkční objekt. Funkci proto můžeme předávat jako parametr jiným funkcím, vložit do seznamu apod. Je to plnohodnotný objekt a jako takový se také chová. Funkční objekt má i několik atributů, jejichž popis však přesahuje rámec našeho seriálu. Případné zájemce odkážu do dokumentace k jazyku.

Pro definování uživatelských funkcí se používá konstrukce def. Za klíčovým slovem def následuje jméno funkce a v závorkách seznam formálních parametrů oddělených čárkou. Celý řádek je ukončen dvojtečkou. Na dalších řádcích následuje tělo funkce odsazené od kraje podobně jako tělo konstrukce if nebo for. Nejjednodušší definice funkce tedy může vypadat takto:

>>> def tiskni_text(answer, text):
...     print answer, text or 'prazdny retezec'
...
>>> tiskni_text('Parametr text je', '')
Parametr text je prazdny retezec
>>> tiskni_text('Parametr text je', 'www.root.cz')
Parametr text je www.root.cz

Funkce také nemusí mít žádné parametry, pak jsou místo výčtu formálních parametrů uvedeny pouze závorky, např. getmax(). V Pythonu existují i nepovinné parametry, které mohou být při volání funkce vynechány, mají zadánu výchozí hodnotu, která je použita, není-li tento formální parametr použit. Nepovinných formálních parametrů může být i více, ale vždy musí platit: všechny nepovinné formální parametry v definici funkce následují po parametrech povinných. Je také třeba mít na paměti, že výraz, který přiřazujeme nepovinnému formálnímu parametru, je vyhodnocen jen při definici funkce (jeho hodnota je dále uchována ve funkčním objektu)!

>>> def tiskni_text(answer, text = 'nedefinovan', pocet = 1):
...     for foo in range(pocet):
...         print answer, text or 'prazdny retezec'
...
>>> tiskni_text('Parametr text je')
Parametr text je nedefinovan
>>> tiskni_text('Parametr text je', 'obycejny text')
Parametr text je obycejny text
>>> tiskni_text('Parametr text je', 'obycejny text', 3)
Parametr text je obycejny text
Parametr text je obycejny text
Parametr text je obycejny text

Výše uvedený způsob volání funkce je stejný jako v jiných jazycích, za jménem funkce následuje výpis skutečných parametrů. Tyto parametry nazýváme pozičními. Python zná ještě keyword parametry (parametry používající klíčová slova). Volání funkce tiskni_text můžeme za použití keyword parametrů zapsat takto:

>>> tiskni_text(answer = 'Parametr text je', text = 'obycejny text')
Parametr text je obycejny text

Keyword parametry můžeme psát i napřeskáčku, tj. v jiném pořadí, než jsou zapsány při definici funkce. Použijeme jich, chceme-li hodnotu přiřadit jen určitému nepovinnému parametru. Chceme-li třikrát vytisknout text ‚Parametr text je nedefinován‘, použijeme zápis:

>>> tiskni_text('Parametr text je', pocet = 3)
Parametr text je nedefinovan
Parametr text je nedefinovan
Parametr text je nedefinovan

Zde jsme zkombinovali poziční a keyword parametry. Opět musí platit několik pravidel. Především – zápis pozičních parametrů musí předcházet zápisu keyword parametrů. Dále hodnota předaná pomocí pozičního parametru nesmí být zároveň předána keyword argumentem. Zápis

>>> tiskni_text('Hello world', answer = 'Nazdar svete', pocet = 2)

vede k výjimce. Stejně tak musí být při volání naplněny všechny povinné formální parametry.

Co se ale stane, když funkci předáme nějaké poziční nebo keyword parametry navíc? U syntaxe, kterou jsme si ukázali v předchozích odstavcích, dojde k výjimce. Je ale možné použít dvě speciální formy zápisu formálních parametrů: *identifikator a **identifikator, přičemž je nemusíme použít vůbec nebo můžeme použít jen jeden, popř. můžeme použít oba dva. Do proměnné identifikator jsou při použití syntaxe *identifikator uloženy všechny přebývající poziční parametry, při syntaxi **identifikator všechny přebývající keyword parametry. Poziční parametry jsou uloženy jako typ tuple, keyword parametry jako asociativní pole, jehož indexy jsou jména keyword parametrů a hodnotami jsou vlastní skutečné parametry předané funkci formou keyword parametrů.

>>> def tiskni_text(answer, text = 'nedefinovan', *args, **kwargs):
...     print answer, text or 'prazdny retezec'
...     print 'zbyvajici pozicni parametry:', args
...     print 'zbyvajici keyword parametry:', kwargs
...
>>> tiskni_text('Parametr text je', 'linux', 'Ahoj', server = 'www.root.cz')
Parametr text je linux
zbyvajici pozicni parametry: ('Ahoj',)
zbyvajici keyword parametry: {'server': 'www.root.cz'}

I při volání funkce můžeme jako skutečné parametry použít syntaxi *identifikator a **identifikator. Hodnoty z těchto parametrů pak budou „sloučeny“ s pozičními a keyword parametry předanými funkci. Zápis hvězdičkovaných parametrů musí být uveden po výčtu všech pozičních a keyword parametrů a nejprve musí být uveden (je-li použit) *identifikator, až poté (je-li použit) **identifikator. Klíči asociativního pole, které má sloužit jako **identifikator, musí být řetězce, jinak dojde k výjimce. Jako ukázka nechť slouží následující volání funkce tiskni_text:

>>> pozicni = ('prvni', 'druhy', 'treti')
>>> keyword = {'os': 'linux'}
>>> tiskni_text('Parametr text je', 'linux', 'Ahoj', server  = 'www.root.cz', \
... *pozicni, **keyword)
Parametr text je linux
zbyvajici pozicni parametry: ('Ahoj', 'prvni', 'druhy', 'treti')
zbyvajici keyword parametry: {'server': 'www.root.cz', 'os': 'linux'}

Použití konstrukce global

Jak jsme si již řekli, proměnné z globálního prostoru jmen jsou přístupné pro čtení kdykoli, ale chceme-li jim přiřadit nějakou hodnotu, musíme je deklarovat jako globální. Největší význam to má právě při použití s funkcemi a metodami (popravdě řečeno, jiné použití mě ani nenapadá, jestli víte ještě o jiném, mně skrytém použití konstrukce global, podělte se s námi v diskusi).

>>> g = 0                               # (1)
>>> def f():                            # (2)
...     g = 1                           # (3)
...     print 'f(): g =', g             # (4)
...                                     # (5)
>>> f()                                 # (6)
f(): g = 1
>>> print 'global: g =', g              # (7)
global: g = 0
>>> def f2():                           # (8)
...     global g                        # (9)
...     g = 2                           # (10)
...     print 'f2(): g =', g            # (11)
...                                     # (12)
>>> f2()                                # (13)
f2(): g = 2
>>> print 'global: g =', g              # (14)
global: g = 2

Na začátku naší ukázky jsme si vytvořili proměnnou g. Provedli jsme to v hlavním bloku modulu, takže g je uloženo v globálním prostoru jmen. Na řádku (2) jsme si vytvořili novou funkci f(), která proměnné g přiřadí hodnotu 1 a ještě hodnotu g vytiskne. Na řádku (6) funkci f() zavoláme a zjistíme, že uvnitř jejího těla má proměnná g opravdu hodnotu 1. Na řádku (7) si zkontrolujeme hodnotu g, ale jaké je překvapení, že má pořád hodnotu 0. Vysvětlení je jednoduché. Přiřazení na řádku (3), místo aby změnilo hodnotu globální proměnné g, vytvořilo novou lokální proměnnou, která překryla globální, a proto i kontrolní tisk na řádku (4) vytiskl hodnotu 1. Hodnota globální proměnné g ale zůstala nezměněna. To řeší až funkce f2(), která používá konstrukci global. Pak již vše pracuje, jak má.

Návratová hodnota funkce

Návrat z uživatelské funkce provede konstrukce return, které mohou být předány argumenty, jež se použijí jako návratová hodnota funkce. Return bez argumentů, stejně jako ukončení funkce bez konstrukce return, vrátí hodnotu None, což je vestavěná proměnná s významem podobným, jaký má v céčku null.

CS24_early

Výjimky a funkce

Vznikne-li ve funkci neošetřená výjimka, rozšíří se z této funkce do funkce volající atd., dokud není odchycena. Nedojde-li k odchycení výjimky, a ta se rozšíří až do hlavního modulu, je standardní akcí výpis volaných funkcí, informací o výjimce a ukončení programu. Více si o výjimkách povíme, až budeme probírat třídy a jejich používání. Výjimky totiž nejsou nic jiného než třídy, takže je u nich možno používat dědičnost apod.

Příště

V dalším díle seriálu, určeného pro nováčky v Pythonu, si probereme moduly a balíčky. Povíme si něco i o tečkové notaci, která se používá pro přístup k atributům objektů.

Byl pro vás článek přínosný?