Путаница с глобальной переменной/областью Python

Я начал изучать python и заметил что-то странное с глобальными переменными и областью видимости. Когда я запускаю это:

x = 2 
y = 3 
z=17 
def add_nums():
    y = 6 
    return z+y

Результат 23 печатается... Однако, когда я расширяю возврат, чтобы быть:

x = 2 
y = 3 
z=17 
def add_nums(): 
    y = 6 
    z = z + y 
    return z

Я получаю следующую ошибку в строке 6:

Local name referenced but not bound to a value.
A local name was used before it was created. You need to define the     
method or variable before you try to use it.

Я не понимаю, почему я получаю здесь ошибку, поскольку z является глобальным и доступным.


person Vaderico    schedule 25.05.2015    source источник
comment
Явный лучше, чем неявный используйте ключевое слово global после имени функции, python в этом случае предполагается, что z является локальным   -  person mjb4    schedule 25.05.2015
comment
Я думаю, что на тот же вопрос был дан правильный ответ здесь -them" title="использование глобальных переменных в функции, отличной от той, которая их создала">stackoverflow.com/questions/423379/ уже   -  person Ales Maticic    schedule 25.05.2015


Ответы (4)


Когда переменная находится слева от знака равенства, Python создаст локальную переменную. Когда переменная находится справа от знака равенства, python попытается найти локальную переменную, и если он не сможет ее найти, он будет использовать глобальную переменную. В вашем примере z находится справа и слева от знака равенства, чтобы избежать двусмысленности, python вызывает ошибку. Вам нужно использовать синтаксис global, чтобы избежать этого:

x = 2 
y = 3 
z=17 
def add_nums(): 
    global z
    y = 6 
    z = z + y 
    return z
person João Abrantes    schedule 25.05.2015

Python определяет область действия путем связывания операций. Назначение — это операция связывания, как и импорт, использование имени в качестве цели в цикле except .. as, with .. as или for, либо создание функции или класса.

Когда имя привязано к области, оно является локальным для этой области. Если имя используется, но не привязано, оно не является локальным; компилятор определит во время компиляции, какой должна быть область действия. В вашем случае нет родительской области, кроме глобальной, поэтому любое имя, не связанное с ним, считается глобальным.

Поскольку ваш второй пример привязывается к z (вы использовали z =, присваивание), имя является локальным для функции.

Если имя привязано к области видимости, но вы хотите сообщить Python, что оно все равно должно быть глобальным, вам нужно сделать это явно:

x = 2 
y = 3 
z=17 
def add_nums(): 
    global z
    y = 6 
    z = z + y 
    return z

Строка global z сообщает компилятору, что z должно быть глобальным, даже если вы привязываетесь к нему.

person Martijn Pieters    schedule 25.05.2015
comment
Привет @Martijn Питерс. Можно ли привязать переменную локально после того, как вы объявили ее глобально в функции? Обычно я бы просто использовал другую переменную, я просто прошу только в образовательных целях (подталкивая, что возможно, а что нет). Например, в приведенном выше примере после выполнения global z и изменения z этот глобальный z теперь имеет значение 23. Что, если я захочу перед строкой return z перепривязать ее локально под тем же именем z (как бы сделать ее копию под тем же именем) и сделать с ней кучу вещей и вернуть значение копии. АФАИК это невозможно. - person Marius Mucenicu; 10.06.2019
comment
@ Джордж, это невозможно. Область действия переменной устанавливается во время компиляции и не может быть динамически изменена во время выполнения, а не во время выполнения самого объекта кода, который использует переменную. - person Martijn Pieters; 10.06.2019
comment
@George, если все, что вы хотите сделать, это иметь локальную копию глобального значения для возврата в конце функции, используйте локальную переменную с другим именем. z_local = z если `z ссылается на неизменяемый тип, этого достаточно. - person Martijn Pieters; 10.06.2019
comment
Спасибо за ваш ответ @Martijn Pieters! Я думал, что во время компиляции оценивается только заголовок функции, а тело во время выполнения, моя информация кажется неверной. Таким образом, тело функции анализируется во время компиляции с точки зрения того, что есть что, но не оценивается, как при уменьшении значения выражений до их атомарного значения и так далее. - person Marius Mucenicu; 10.06.2019

Проанализируем отмеченную линию.

x = 2
y = 3 
z = 17 
def add_nums(): 
    y = 6 
    z = z + y  <--- THIS LINE
    return z

z ... создается новая локальная переменная
= ... мы собираемся присвоить ей значение
z ... эта переменная существует (новая локальная), но пока не имеет значения.< br> +y... до этой части дело не дошло.

Результатом является сообщение об ошибке «UnboundLocalError: ссылка на локальную переменную 'z' перед назначением».

person dlask    schedule 25.05.2015

Если вы хотите, чтобы имена y и z внутри тела функции ссылались на ваши глобальные переменные когда вы что-то им присваиваете, вы должны объявить их следующим образом:

x = 2 
y = 3 
z=17 
def add_nums(): 
    global y 
    global z 
    y = 6
    z = z + y
    return z

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

Как указано в комментариях, вы можете ссылаться на глобальную переменную и даже изменять ее, если она является изменчивой (т.е. добавлять что-то в список), не объявляя ее явно глобальной, если вы не пытаетесь присвоить ей.

person Eric Appelt    schedule 25.05.2015
comment
Это не совсем точно, так как вы можете без проблем ссылаться на y таким образом. - person SuperBiasedMan; 25.05.2015
comment
Если вы просто пишете y = 6 в функции, вы вообще не ссылаетесь на глобальное y, а просто объявляете локальное имя y и привязываете к нему 6. Вы можете увидеть разницу, если напечатаете y после запуска функции. - person Eric Appelt; 25.05.2015
comment
Извините, запутался в своем комментарии. В его первом примере не нужно ссылаться на z, и он все равно будет работать. Вам нужно явно объявить глобальные переменные только в том случае, если Python может подумать, что вы объявляете новую локальную переменную. - person SuperBiasedMan; 25.05.2015
comment
Я понимаю вашу точку зрения - вы правы в том, что исходный пост не совсем точен, и я попытался отредактировать его, чтобы исправить это. - person Eric Appelt; 25.05.2015