[Tutor PyCZ] Globalni, lokalni a volne promenno (bylo Dalsi problem s Livewires)

Petr Prikryl PrikrylP na skil.cz
Středa Březen 1 09:19:21 CET 2006


geon
> Tomáš Bělonožník napsal(a):
> > [...]
> > def posunHrace():
> >     stisky = keys_pressed()
> >     if "2" in stisky:
> >         yh = yh - 10
> >         move_to(Hrac_telo, xh, yh)
> >         return yh
> >
> > [...] Hází mi to ale chybu "local variable 'yh' 
> > referenced before assignment". Chápu to
> > tak, že jsem proměnnou yh zmínil ještě předtím, 
> > než jsem jí přiřadil nějakou hodnotu. Ale proč 
> > si tu hodnotu nevezme sám z vyšších pater
> > programu, jako to udělal, když řádek move_to vypadal
> > "move_to(Hrac_telo, xh, yh-10)". 

> [...] Trocha teorie ode mne: některé proměnné 
> skutečně funkce dokáže brát zezhora - zde třeba Hrac_telo 
> nebo tvůj výše uváděný move_to(Hrac_telo, xh, yh-10). 
> Jsou to ale vzdy jen ty proměnné, které se ve funkci 
> nemění - jsou tam jen jakoby read-only. Proto ti to 
> házelo tu chybu, protože jsi chtěl změnit read-only 
> proměnnou.

Pokud ve funkci použiji proměnnou a nesnažím
se do ní přiřazovat, jde o takzvanou volnou proměnnou
(free variable). Volnou se nazývá proto, protože
v bloku kódu neexistuje pevná vazba mezi jménem
'x' a konkrétním místem, kde je hodnota uložena.
V okamžiku odkazu se teprve poloha "ve vyšších patrech"
bude zjišťovat.

Pokud do proměnné něco přiřadím, stává se lokální 
proměnnou v daném bloku kódu. Vyytváří se pevná
vazba z mého lokálního prostoru. Už nemůžeme hovořit
o volné proměnné (tj. o proměnné definované někde
ve vyšších patrech). Pevná vazba na lokální prostor
se vytváří i tehdy, když se do této proměnné
přiřazuje až později (ve stejném bloku), než když 
se z ní čte. Příklad:

soubor a.py 
=====================================
x = 10

def funkce1():
    print x

def funkce2():
    return x

print x            # (1)
funkce1()          # (2)
print funkce2()    # (3)
=====================================

Příkaz "print x" na řádku (1) vytiskne
podle očekávání hodnotu 10. Jde o proměnnou
definovanou na stejné úrovni.

Na řádku (2) voláme funkce1() a ta se
v příkazu "print x" tiskne proměnou, která
je definována na "vyšší úrovni". Jde ale
o volnou proměnnou, která se postupně hledá
v nadřízených lokálních prostorech (zde
žádné nemáme) a nakonec v prostoru globálním.

Na řádku (3) tiskneme výsledek, který získáváme
voláním funkce2(). V jejím těle se pouze vrací
hodnota volné proměnné x. Jde o podobný případ,
jako u funkce1(). Hodnota proměnné x se pouze
zpracuje jiným způsobem.

Zkuste ale tohle:

soubor c.py 
=====================================
x = 10

def funkce3():
    x = x + 1
    print x

funkce3()
=====================================

Při pokusu o spuštění se ukáže toto...

C:\tmp>python c.py
Traceback (most recent call last):
  File "c.py", line 7, in ?
    funkce3()
  File "c.py", line 4, in funkce3
    x = x + 1
UnboundLocalError: local variable 'x' referenced before assignment

Jak už bylo uvedeno výše, přiřazení "x = x + 1" 
způsobí, že funkce3 už o proměnné x chce uvažovat
jako o lokální proměnné. Jenže tato lokální proměnná
je použita na pravé straně přiřazení a zatím jí
nebyla přiřazena žádná hodnota, tj. "proveden
odkaz na lokální proměnnou 'x', které dosud nebylo 
nic přiřazeno". Ona vlastně proměnná x zatím 
neexistuje a chceme ji vytvořit a přiřadit
jí její neexistující hodnotu zvětšenou o jedničku.

Ve stejném bloku kódu označuje jedno jméno
jen jednu proměnnou. Python nedovolí, abychom
se na tuto proměnnou dívali chvíli jako na volnou
proměnnou (vytvořenou ve vyšších patrech) a o něco
později jako na lokální proměnnou. Z tohoto důvodu
selže i následující příklad:

soubor d.py 
=====================================
x = 10

def funkce4():
    print x
    x = x + 1

funkce4()
=====================================

C:\tmp\a>python d.py
Traceback (most recent call last):
  File "d.py", line 7, in ?
    funkce4()
  File "d.py", line 4, in funkce4
    print x
UnboundLocalError: local variable 'x' referenced before assignment

Tentokrát selže už příkaz "print x", který
ve funkce1() normálně fungoval. Tehdy jsme se
ale na x dívali jako na volnou proměnnou 
(definovanou jinde). Jenže příkaz "x = x + 1"
ve funkce4() způsobí, že x nemůže být volnou
proměnnou. V okamžiku definice funkce4() už se
rozhodne o tom, že x bude lokální proměnná
a nikdy jinak. Za těchto okolností se na příkaz
"print x" pohlíží jako na pokus zobrazit obsah
dosud neexistující proměnné.

Pokud by Python nebyl tak přísný a měnil
by charakter proměnné z volné na lokální
až podle potřeby, mohlo by to fungovat, 
ale pro člověka by se zdrojový text stával
nepřehledným a v komplikovanějších případech 
by to mohlo vést k těžko odhalitelným chybám. 
Už jen v případě "x = x + 1" bychom vlastně
nemohli říci, co ten příkaz dělá, protože
bychom neměli jistotu, že se na obou stranách
přiřazení pracuje se stejnou proměnnou x.

pepr


Další informace o konferenci Tutor