Proměnné, tabulky symbolů a rozsahy platnosti v Jythonu

 

V dnešní dílu se budeme zabývat otázkou, jak dlouho je platná definice proměnné či funkce, a z kterých míst jsou tyto definice přístupné. Jinými slovy, nastal čas si vážně pohovořit o jmenných prostorech.

Pro sledování rozsahu platnosti definic používá Jython takzvané jmenné prostory. Jsou to tabulky symbolů, které si můžeme představit jako asociativní seznam názvů proměnných a s nimi svázaných hodnot. V libovolném časovém okamžiku má Jython k dispozici několik jmenných prostorů.

Každá funkce má svůj jmenný prostor nazývaný lokální jmenný prostor (anglicky local namespace), který obsahuje přehled o všech lokálních proměnných včetně jejích argumentů.

Každý modul svůj jmenný prostor nazývaný globální jmenný prostor (anglicky global namespace) obsahující přehled všech proměnných definovaných v modulu, definicí funkcí, tříd a všech importovaných modulů.

Posledním ze jmenných prostorů je tzv. vestavěný jmenný prostor (anglicky built-in namespace). Tento jmenný prostor (korespondující s vestavěným modulem __builtin__) je přístupný ze všech modulů a obsahuje definice všech vestavěných funkcí a výjimek.

Kdykoliv se interpret dotazuje na hodnotu proměnné x, prohledává všechny dostupné jmenné prostory v následujícím pořadí:

  1. Lokální jmenný prostor – patří konkrétní funkci nebo definici metody. Pokud funkce obsahuje lokální proměnnou x nebo parametr x, Jython tuto proměnnou použije a nepokračuje dál v hledání.
  2. Globální jmenný prostor – náleží konkrétnímu modulu. Pokud modul obsahuje proměnnou, funkci nebo třídu nazvanou x, Jython tuto proměnnou použije a nepokračuje dál v hledání.
  3. Vestavěný jmenný prostor – globální, přístupný všem modulům. Jako poslední východisko Jython předpokládá, že x je název vestavěnné funkce nebo proměnné.

Pokud Jython nenajde název x v žádném z uvedených jmenných prostorů, vyvolá výjimku NameError se zprávou: There is no variable named 'x'.

Výše uvedenému mechanismu vyhledávání názvů objektů je důležité porozumět, abychom se vyvarovali chyb souvisejících s platností definic proměnných. Podle tohoto konceptu tedy existují tři rozsahy platnosti: lokální, globální a __builtin__.

Rozsahy platnosti se do sebe nevnořují. Toto tvrzení platí do verze 2.1 a má dalekosáhlé důsledky. Vyplývá z něho, že definice funkcí vnořené do jiné definice se nemohou odvolávat na proměnné nacházející se vně tohoto bloku.

>>> def vnejsiFce(x):
... print x
...
... def vnitrniFce():
... x = x + 1
... print x
...
<console>:1: SyntaxWarning: local name 'x' in 'vnejsiFce' shadows use as global in nested scopes

Lze to obejít následující konstrukcí:

>>> def vnejsiFce(x):
... print x
... def vnitrniFce(x = x):
... x = x + 1
... print x
... vnitrniFce()
...
>>> vnejsiFce(10)
10
11

, která je ale poněkud těžkopádná.

Takovému rozsahu platnosti, jaký je používán v Jythonu (do verze 2.1 včetně), se také říká statický rozsah platnosti. Nová verze Jythonu (verze 2.2.1) používá jinou definici rozsahu platnosti, které se říká staticky vnořený rozsah platnosti, nebo též lexikální rozsah platnosti.

Zde již je možné použít následující kód:

>>> def vnejsiFce(x):
... print x
... def vnitrniFce(x):
... x = x + 1
... print x
... vnitrniFce(x)
...
>>> vnejsiFce(10)
10
11

locals() a globals()

Jmenné prostory jsou během vykonávání programu přístupné pomocí vestavěných funkcí locals() a globals().

>>> def mojeFce(arg):
... x = 1
... print locals()
...
>>> mojeFce('ahoj')
{'arg': 'ahoj', 'x': 1}

Vidíme, že lokální jmenný prostor je reprezentován asociativním seznamem obsahujícím proměnné x a arg s přiřazenými hodnotami 1 a ‘ahoj’.

V Jythonu lze lokální jmenný prostor modifikovat přímo, a to změnou hodnot asociativního pole:

>>> def fce(arg):
... x = 1
... print locals()
...
locals()['x'] = 999 #zmenim hodnotu promenne x
... print "x: ", x
...
>>>
>>> fce(111)
{'arg': 111, 'x': 1}
x: 999
>>>

Podobně lze nakládat s globálním jmenným prostorem pomocí funkce globals. Přidejme do modulu obsahujícího definici naší funkce následující řádky:

if __name__ == "__main__":
for k, v in globals().items():
print k, "=", v

, takže jeho obsah bude vypadat následovně:

"""Demonstrace globalniho jmenneho prostoru.
Vypise jeho obsah"""

def mojeFce(arg):
x = 1
print 'arg: ', arg
print 'x: ', x

if __name__ == "__main__":
for k, v in globals().items():
print k, "=", v

a uložme ho.

Spuštěním skriptu z příkazové řádky

D:\Jython\Sourcecodes>globns.py

získáme následující výpis:

__builtins__ = <module '__builtin__' (built-in)>
__name__ = __main__
g_promenna = ja sem globalni pro cely modul
__doc__ = Demostrace globalniho jmenneho prostoru.

Vypise jeho obsah
mojeFce = <function mojeFce at 0x007BDF80>

Vidíme zde názvy a hodnoty všech proměnných definovaných v modulu. Dále můžeme vidět názvy atributů modulu ( __builtins__, __name__, __doc__) a jejich hodnoty (objekt typu modul a řetězec). Proměnná mojeFce obsahuje odkaz na objekt obsahující vlastní implementaci naší funkce.

Pro zjišťování obsahu jmenných prostorů je velmi užitečná funkce dir. Tato funkce vypíše setříděný seznam proměnných definovaných v daném jmenném prostoru. Zavoláme-li funkci dir bez argumentů, vrátí názvy atributů a funkcí definovaných v jmenném prostoru modulu, ve kterém je funkce volána.

Jython 2.1a1 on java1.4.0 (JIT: null)
Type "copyright", "credits" or "license" for more information.
>>> dir()
['__doc__', '__name__']
>>>
>>>
>>> import globns
>>>
>>> dir()
['__doc__', '__name__', 'globns']

Zadáme-li argument název objektu, vrátí funkce seznam atributů a funkcí daného objektu.

>>> dir(globns)
['__doc__', '__file__', '__name__', 'g_promenna', 'mojeFce']

Zajímá-li nás seznam vestavěných funkcí, můžeme je tedy získat aplikováním funkce dir na modul __builtin__:

>>> import __builtin__
>>>
>>> dir(__builtin__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'DeprecationWarning', 'EOFError',
'Ellipsis', 'EnvironmentError', 'Exception', 'FloatingPointError',
'IOError', 'ImportError', 'IndentationError', 'IndexError',
'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError',
'NameError', 'None', 'NotImplementedError', 'OSError',
'OverflowError', 'RuntimeError', 'RuntimeWarning', 'StandardError',
'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit',
'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeError',
'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_',
'__debug__', '__doc__', '__import__', '__name__', 'abs', 'apply',
'callable', 'chr', 'classDictInit', 'cmp', 'coerce', 'compile',
'complex', 'copyright', 'credits', 'delattr', 'dir', 'divmod',
'eval', 'execfile', 'exit', 'filter', 'float', 'getattr', 'globals',
'hasattr', 'hash', 'hex', 'id', 'input', 'int', 'intern',
'isinstance', 'issubclass', 'len', 'license', 'list', 'locals',
'long', 'map', 'max', 'min', 'oct', 'open', 'ord', 'pow', 'quit',
'range', 'raw_input', 'reduce', 'reload', 'repr', 'round', 'setattr',
'slice', 'str', 'tuple', 'type', 'unichr', 'unicode', 'vars',
'xrange', 'zip']