InterSystems B.V.
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í:
- 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í. - 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í. - 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']