глобальная переменная python не работает в функции, когда она оценивается оператором if в функции

Пример 1:

a=1
def b():
    print a
    a=2
    print a

Пример 2:

a=1
def b():
    print a
    if a==1:
        a=2
    print a

Пример 1 работает, как и ожидалось, пример 2 завершается с ошибкой UnboundLocalError: ссылка на локальную переменную 'a' перед назначением при первом выводе a

Может кто-нибудь объяснить, почему это происходит? Это ошибка или особенность?

Второй пример не очень полезен, но я не понимаю, почему он не должен работать. Я ожидаю, что функция b сначала напечатает глобальный a, а затем проверит, равен ли глобальный a 1. Если это правда, локальному a будет присвоено значение 2. Затем будет напечатан либо глобальный a, либо локальный a в зависимости от значения глобального a. Что ж, в примере глобальный а равен 1, поэтому я ожидаю увидеть локальный а со значением 2.

Пример 3:

a=1
def b():
    print a

работает

Пример 4:

a=1
def b():
    print a
    a=2

терпит неудачу, как и пример 1, как правильно прокомментировано, на самом деле я тестировал только пример 3 и думал, что он такой же, как пример 1.

Теперь я понимаю то, что так часто повторялось в отношении всего масштаба. Это совершенно новое для меня, и я получаю удовольствие от следующего примера (Ipython):

In [13]: a=1

In [14]: def b():
   ....:     a=2
   ....:     global a
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
   ....:     print a
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
   ....:     
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<input>:3: SyntaxWarning: name 'a' is assigned to before global declaration
<ipython-input-14-4fe1c6cdc9d0>:3: SyntaxWarning: name 'a' is assigned to before global     
declaration
  global a

In [15]: b()
2

In [16]: print a
2

Таким образом, механизм отличается от объявления функции или подобных вещей, когда я могу получить доступ к состоянию только после того, как я его изменил.

Существуют ли какие-либо другие действия в питоне, которые работают так — путешествие во времени?

Чтобы примеры работали, можно использовать globals():

Пример 1 работает:

a=1
def b():
    print globals()["a"]
    a=2
    print a

Пример 2 работает:

a=1
def b():
    global_a=globals()["a"]
    print global_a
    if global_a==1:
        a=2
    print a

Пример 4 работает:

a=1
def b():
    print globals()["a"]
    a=2

person c.holtermann    schedule 08.06.2014    source источник
comment
Пример 1 также завершается ошибкой с UnboundLocalError.   -  person Martijn Pieters    schedule 09.06.2014
comment
Я понимаю. Я думал, что запустил первый пример, но это не так.   -  person c.holtermann    schedule 09.06.2014


Ответы (1)


Python определяет область действия имени на основе присваивания во время синтаксического анализа. Если вы назначаете имя, оно является локальным для всей области действия. Вы назначаете a в b(), так что это локальное имя, оно никогда не рассматривается как глобальное имя.

В таком случае вам придется явно указать синтаксическому анализатору обрабатывать a как глобальное значение:

def b():
    global a
    print a
    if a==1:
        a=2
    print a

Обратите внимание, что ваш первый пример завершается с той же ошибкой; вы также назначаете в этой версии, поэтому она тоже поднимает UnboundLocalError:\

>>> a=1
>>> def b():
...     print a
...     a=2
...     print a
... 
>>> b()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in b
UnboundLocalError: local variable 'a' referenced before assignment
person Martijn Pieters    schedule 08.06.2014
comment
ни один из его примеров не будет работать - person Padraic Cunningham; 09.06.2014
comment
@PadraicCunningham не уверен, почему я прочитал это как ==. Но вы правы, первый тоже не работает. - person Martijn Pieters; 09.06.2014
comment
Я ожидал, что смогу использовать переменную глобально (доступ для чтения) до тех пор, пока не сделаю локальное назначение. Но очевидно, что последнее назначение останавливает эту возможность. - person c.holtermann; 09.06.2014
comment
Таким образом, для всей области также используется обратный код. Я ожидал, что логика будет переходить от строки к строке, так что она становится локальной только для строк после присваивания. Но для всего размаха явно работает по-разному. - person c.holtermann; 09.06.2014