Локальные и глобальные переменные Python

Я понимаю концепцию локальных и глобальных переменных в Python, но у меня просто возникает вопрос, почему ошибка возникает так, как она есть в следующем коде. Python выполняет коды по строкам, поэтому он не знает, что a - это локальная переменная, пока не будет прочитана строка 5. Остается ли Python вернуться к одной строке и пометить ее как ошибку после попытки выполнить строку 5?

a=0

def test():
    print a  #line 4, Error : local variable 'a' referenced before assignment
    a=0      #line 5

test()

Ответ 1

Настройка и тестирование

Чтобы проанализировать ваш вопрос, создайте две отдельные тестовые функции, которые реплицируют вашу проблему:

a=0

def test1():
    print(a)

test1()

печатает 0. Таким образом, вызов этой функции не проблематичен, а в следующей функции:

def test2():
    print(a)  # Error : local variable 'a' referenced before assignment
    a=0  

test2()

Мы получаем сообщение об ошибке:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in test2
UnboundLocalError: local variable 'a' referenced before assignment

Демонтажные

Мы можем разобрать две функции (первая Python 2):

>>> import dis
>>> dis.dis(test1)
  2           0 LOAD_GLOBAL              0 (a)
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       
              5 LOAD_CONST               0 (None)
              8 RETURN_VALUE        

И мы видим, что первая функция автоматически загружает глобальную a, а вторая функция:

>>> dis.dis(test2)
  2           0 LOAD_FAST                0 (a)
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       

  3           5 LOAD_CONST               1 (0)
              8 STORE_FAST               0 (a)
             11 LOAD_CONST               0 (None)
             14 RETURN_VALUE      

видя, что внутри него назначается a, пытается выполнить LOAD_FAST из локальных сетей (в целях оптимизации, поскольку функции предварительно скомпилированы в байтовый код перед запуском.)

Если мы запустим это в Python 3, мы получим почти такой же эффект:

>>> test2()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in test2
UnboundLocalError: local variable 'a' referenced before assignment
>>> 
>>> import dis
>>> dis.dis(test1)
  2           0 LOAD_GLOBAL              0 (print) 
              3 LOAD_GLOBAL              1 (a) 
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
              9 POP_TOP              
             10 LOAD_CONST               0 (None) 
             13 RETURN_VALUE     

>>> dis.dis() # disassembles the last stack trace
  2           0 LOAD_GLOBAL              0 (print) 
    -->       3 LOAD_FAST                0 (a) 
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
              9 POP_TOP              

  3          10 LOAD_CONST               1 (0) 
             13 STORE_FAST               0 (a) 
             16 LOAD_CONST               0 (None) 
             19 RETURN_VALUE        

Мы снова видим ошибку на LOAD_FAST.

Ответ 2

Python не выполняет строку за строкой в ​​представленном вами коде функции. Он должен сначала разобрать его как блок исполнения. Он решает, является ли переменная локальной или глобальной в зависимости от того, написана ли она на (функциональном) локальном уровне. Как это имеет место, он решает, что переменная локальна, следовательно, ошибка.

Ответ 3

Это потому, что в python вам нужно сказать, что вы собираетесь изменить значение глобальной переменной. Вы делаете это как:

def test():
  global a
  print a  #line 4, Error : local variable 'a' referenced before assignment
  a=0      #line 5

С помощью global a вы можете изменять переменные в функции. Вы можете посмотреть на это:

Ответ 4

Краткое объяснение:

a переменная привязана к функции test. Когда вы пытаетесь распечатать его, Python выдает ошибку, потому что после этого будет инициализирована переменная локальная a. Но если вы удалите a=0, код будет выполняться без каких-либо проблем и вывести 0.

Более длинное объяснение

Python-интерпретатор начинает поиск переменной с именем a из локальной области. Он видит, что a действительно объявлен внутри функции и инициализирован только на пятой строке. Как только он будет найден, поиск завершен.

Когда он пытается обработать print a (четвертая строка), он говорит: "О, мальчик, мне нужно напечатать переменную, которая еще не инициализирована, я бы лучше выбросил ошибку".

Объяснение на основе байт-кода функции

Если вы запустите этот код:

import dis

a = 0

def test():
  print a  #line 4, Error : local variable 'a' referenced before assignment
  a = 0    #line 5

dis.dis(test)

вы получите этот вывод:

  6           0 LOAD_FAST                0 (a)
              3 PRINT_ITEM          
              4 PRINT_NEWLINE       

  7           5 LOAD_CONST               1 (0)
              8 STORE_FAST               0 (a)
             11 LOAD_CONST               0 (None)
             14 RETURN_VALUE        

Объяснение непонятных частей вышеизложенного:

  • LOAD_FAST означает, что ссылка a помещается в стек.
  • STORE_FAST означает, что Python присваивает 0 (от LOAD_CONST) локальной переменной a

Итак, проблема в том, что LOAD_FAST идет до STORE_FAST.