Edit detail for UnboundLocalError revision 1 of 1

1
Editor: geon
Time: 2006/05/25 15:57:28 GMT+0
Note: oprava chyb

changed:
-
Vysvětlení

 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. Vytváří 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()

Po spuštění dostáváme:: 

    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.


Možná řešení

 Jako mustr si vemme::

    # soubor c.py 

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




 Důsledná lokalizace

  To je asi nejméně častý případ. Proměnná x má být ve funkci lokální a po opuštění funkce zaniká (tam venku ji nepotřebujeme). V tomto případě je to obzvláště nesmyslné, ale někdy to tak může být myšleno::

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

 Oškivá globalizace

  Globalizováním proměnné x ve funkci ji spojíme s proměnnou x definovanou v hlavní části programu. Je to ale nečisté a nedoporučované řešení::

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

 Předáváním a vracením proměnné

  Proměnnou pokaždé do funkce předáme a na konci ji zase vrátíme::

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

 Použitím měnitelných typů

  Seznamy, třídy a další se mohou v těchto případěch uplatnit jako nosiče proměnných, které se dají měnit všude::

    x = [10]
    
    def funkce3():
        x[0] = x[0] + 1
        print x[0]
    
    funkce3()   

Vysvětlení

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. Vytváří 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()

Po spuštění dostáváme:

    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.

Možná řešení

Jako mustr si vemme:

    # soubor c.py 

    x = 10

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

    funkce3()    

Důsledná lokalizace

To je asi nejméně častý případ. Proměnná x má být ve funkci lokální a po opuštění funkce zaniká (tam venku ji nepotřebujeme). V tomto případě je to obzvláště nesmyslné, ale někdy to tak může být myšleno:

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

Oškivá globalizace

Globalizováním proměnné x ve funkci ji spojíme s proměnnou x definovanou v hlavní části programu. Je to ale nečisté a nedoporučované řešení:

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

Předáváním a vracením proměnné

Proměnnou pokaždé do funkce předáme a na konci ji zase vrátíme:

    x = 10

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

    x = funkce3(x) 

Použitím měnitelných typů

Seznamy, třídy a další se mohou v těchto případěch uplatnit jako nosiče proměnných, které se dají měnit všude:

    x = [10]

    def funkce3():
        x[0] = x[0] + 1
        print x[0]

    funkce3()