Níže uvedený text pochází z prvního vydání. Nad tímto textem se nachází aktuální stav po revizi směřující k druhému vydání.

Prostory jmen

Úvod

Už slyším, jak se ptáte… Co to je ten prostor jmen (namespace)? No, dá se to těžko vysvětlit. Ne proto, že by to bylo nějak zvlášť komplikované, ale spíš proto, že se k tomu každý jazyk staví trochu jinak. Samotný koncept je docela přímočarý. Prostor jmen je prostor nebo oblast uvnitř programu, kde je jméno (proměnné, třídy, atd.) platné.

Dřívější programovací jazyky (jako třeba BASIC) pracovaly pouze s globálními proměnnými, to znamená s takovými proměnnými, které byly vidět z celého programu — dokonce uvnitř funkcí. To činilo udržovatelnost programů velmi obtížnou, protože pro každý kousek programu bylo velmi snadné změnit nějakou proměnnou, aniž by se to ostatní části programu nějak dozvěděly. Tomuto jevu se říká vedlejší efekt. Novější jazyky (včetně moderních verzí jazyka BASIC) tento problém obcházejí zavedením prostorů jmen. (Jazyk C++ jde v tomto směru do extrému tím, že umožňuje programátorovi vytvořit svůj vlastní prostor jmen kdekoliv uvnitř programu. To ocení zvláště tvůrci knihoven, kteří chtějí dosáhnout jednoznačnosti jmen svých funkcí i v případě, kdy se současně použijí knihovny jiných tvůrců.)

Jak to řeší Python?

V systému Python vytváří každý modul svůj vlastní prostor jmen. Pokud chceme používat jména jeho částí, musíme jim buď předřadit jméno modulu, nebo musíme explicitně importovat požadovaná jména dovniř prostoru jmen našeho modulu. Není to pro nás nic nového. Už jsme to dělali při práci s moduly sys a string. V určitém smyslu vytváří svůj prostor jmen i definice třídy. Takže pokud chceme zpřístupnit metodu nebo vlastnost třídy, musíme nejdříve použít jméno instance nebo třídy.

V Pythonu existují jen 3 prostory jmen (nebo rozsahy platnosti — scopes):

  1. Lokální rozsah — jména definovaná uvnitř funkce nebo metody.
  2. Rozsah v rámci modulu — jména definovaná uvnitř souboru modulu.
  3. Zabudovaná jména — jména definovaná uvnitř samotného systému Python, která jsou přístupná vždy.

No dobrá. Ale jak to vše dáme dohromady, když proměnné v různých prostorech jmen mají stejné jméno? A nebo, jak se odkazujeme na jméno, které se nenachází v aktuálním prostoru jmen? Podívejme se nejdříve na první případ: Pokud se funkce odvolává na proměnnou nazvanou X a uvnitř funkce existuje nějaká proměnná X (tj. uvnitř prostoru s lokálními proměnnými), pak to bude právě tato lokální proměnná, kterou Python uvidí a použije. Je věcí programátora, aby se vyhnul střetům jmen, kdy má nějaká lokální proměnná stejné jméno jako proměnná modulu a kdy bychom mohli chtít zpřístupnit obě najednou. Existence lokální proměnné v takovém případě maskuje existenci globální proměnné.

Obecně bychom měli globální proměnné používat co nejméně. Obvykle bývá lepší, když běžnou, lokální proměnnou předáváme jako parametr volané funkce a vrací se nám s modifikovaným obsahem.

Poznámka překladatele ke globálním proměnným: Z pohledu začátečníka se může zdát používání globálních proměnných velmi výhodné. Jednoduše přece uvedeme jméno proměnné, které je známé ve všech místech programu! V čem je problém? Postupně zjistíte, že těch problémů může být hned několik. Zdánlivá jednoduchost může později věci zkomplikovat:

Druhý případ, kdy se odkazujeme na jméno, které se nenachází mezi lokálními, se řeší následujícím způsobem: Funkce prohlédne svůj lokální prostor. Pokud zde požadované jméno nenalezne, hledá v prostoru modulu. A pokud není nalezeno ani zde, hledá se v prostoru zabudovaných jmen (builtin scope). Jediná nepříjemnost nastane v situaci, kdy chceme přiřadit hodnotu externí proměnné. Při normálním postupu by vznikla nová proměnná tohoto jména, ale tomu se chceme vyhnout. Takže aby se nevytvořila lokální proměnná daného jména, musíme určit, že se jedná o jméno globální.

Vše si ukážeme v akci na následujícím příkladu (jde o čistě ilustrační příklad):

# Proměnné na úrovni modulu.
W = 5
Y = 3
 
# Parametry se chovají jako proměnné náležející funkci. Takže X patří
# do lokálního prostoru.
def spam(X):
    
   # Funkci oznámíme, že má W hledat na úrovni modulu a nevytvářet svou 
   # proměnnou W.
   global W
   
   Z = X*2 # Nová proměnná Z je vytvořena jako lokální.
   W = X+5 # Práce s W na úrovni modulu -- viz výše.

   if Z > W:
      # pow je jméno 'zabudované' funkce.
      print pow(Z, W)
      return Z
   else:
      return Y # Lokální Y neexistuje, takže se použije globální.

Pokud importujeme modul, jako je například sys, stane se jméno sys lokálně dostupným jménem. Poté můžeme jména uvnitř prostoru jmen modulu sys zpřístupnit použitím takzvaného kvalifikovaného jména, jak jsme si ukázali dříve. (Kvalifikované jméno se od holého liší tím, že holému jménu předřadíme takzvaný kvalifikátor, který má podobu dalšího jména, vhodně spojeného s původním holým jménem. V jazyce Python se obě části oddělují tečkou. Například v jazyce C++ se oddělují dvěma dvojtečkami.)

Pokud napíšeme

from sys import exit

pak v lokálním prostoru jmen zpřístupníme pouze funkci exit. Nemůžeme použít žádné jiné jméno z modulu sys a dokonce ani samotné jméno modulu sys.

Ještě v jazyce BASIC...

BASIC volí ve srovnání z jazykem Python opačný přístup. Všechny vytvořené proměnné se automaticky stávají globálními (aby byla zachována kompatibilita, tedy slučitelnost, s programy psanými pro starší verze jazyka BASIC), ale programátor může vytvářet i proměnné, které jsou lokální uvnitř funkcí, jejich označením klíčovým slovemLOCAL.

Tcl

Asi si mohu dovolit tvrdit, že v Tcl neexistuje žádný mechanismus pro přístup k různým úrovním viditelnosti jmen. Důvodem je asi zvláštní způsob, jakým Tcl program provádí. Všechny proměnné se v každém případě jeví jako lokální vzhledem k jejich nejbližšímu okolí — proměnné na úrovni souboru jsou viditelné pouze pro příkazy uvnitř stejného souboru a proměnné procedur jsou viditelné pouze uvnitř procedur. Komunikaci mezi těmito prostory jmen můžeme zajistit pouze předáváním hodnot v podobě parametrů, při volání zmíněných procedur.


Pokud vás napadne, co by se dalo na překladu této kapitoly vylepšit, zašlete e-mail odklepnutím Tím budou do dopisu automaticky vloženy informace o tomto HTML dokumentu.

$Id: cztutname.html,v 1.5 2004/08/31 11:55:14 prikryl Exp $