Как имя может быть "несвязанным" в Python? Какой код может вызвать `UnboundLocalError`?
Я прочитал следующее в документации Python:
Когда имя вообще не найдено, возникает
NameError
исключение. Если текущая область видимости является областью действия функции, а имя ссылается на локальную переменную, которая еще не была привязана к значению в точке, где используется имя, возникаетUnboundLocalError
исключение.UnboundLocalError
является подклассомNameError
.
...
В Python отсутствуют объявления, и операции привязки имен могут выполняться в любом месте блока кода.
Я не понимаю, как это работает. Если нет объявлений, то когда возникает вопрос UnboundLocalError
? Как переменная "еще не привязана" при ее обнаружении?
Смотрите также UnboundLocalError пытается использовать переменную (предположительно глобальную), которая (повторно) назначается (даже после первого использования) для общей проблемы, когда переменная, которая, как ожидалось, будет глобальной, вместо этого является локальной. Этот вопрос ориентирован на случаи, когда программист ожидает, что переменная будет локальной.
Переведено автоматически
Ответ 1
Вы можете ссылаться на имя, не присваивая ему никакого значения:
>>> foobar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foobar' is not defined
Здесь foobar
упоминается, но никогда не присваивалось. Это вызывает NameError
, потому что имя никогда не было привязано.
Более тонко, здесь присваивание не происходит, потому что строка, которая это делает, никогда не выполняется:
>>> def foo():
... if False:
... spam = 'eggs'
... print(spam)
...
>>> foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in foo
UnboundLocalError: local variable 'spam' referenced before assignment
Поскольку spam = 'eggs'
никогда не выполняется, print(spam)
возникает UnboundLocalError
.
Обратите внимание, что нигде в Python имя никогда не объявляется. Вы привязываете или не привязываете, объявление не является частью языка.
Вместо этого для определения области действия имени используется привязка; операции привязки включают присвоение, имена, используемые для for
цикла, параметры функции, инструкции импорта, имя для хранения перехваченного исключения в except
предложении и имя диспетчера контекста в with
инструкции.
Если имя привязано к области видимости (например, в функции), то это локальное имя, если только вы не используете global
оператор (или nonlocal
оператор в Python 3), чтобы явно пометить имя как глобальное (или замыкание) вместо этого.
Итак, следующее является ошибкой:
>>> foo = None
>>> def bar():
... if False:
... foo = 'spam'
... print(foo)
...
>>> bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in bar
UnboundLocalError: local variable 'foo' referenced before assignment
потому что foo
оно привязывается где-то в bar
области видимости функции. Но если вы пометите foo
как глобальное, функция заработает:
>>> foo = None
>>> def bar():
... global foo
... if False:
... foo = 'spam'
... print(foo)
...
>>> bar()
None
потому что теперь компилятор Python знает, что вы хотели, чтобы foo
вместо этого было глобальным.
Все это задокументировано в разделеИменование и привязка справочной документации Python.
Ответ 2
Еще одно место, когда переменная объявлена в глобальной области видимости, и если она используется в области функции, она должна конкретно называться глобальной, иначе произойдет UnboundLocalError . Ниже приведен пример.:
var = 123
def myfunc():
var = var + 1
print var
myfunc()
Ошибка:
UnboundLocalError: local variable 'var' referenced before assignment
Ответ 3
UnboundLocalError
это особый вид NameError
, который конкретно относится к локальным переменным. Те же логические проблемы, которые вызывают NameError
s в коде вне функций, могут вызывать UnboundLocalError
внутри них.
Обычно проблема возникает при попытке написать условную логику, у которой нет запасного варианта:
import random
def example_if():
if random.random() < 0.5:
result = 1
return result
print(example_if())
Это приведет к сбою всякий раз, когда условие не выполняется, потому что в этом случае не было ничего, что было бы назначено result
, поэтому нет способа использовать это. Здесь важно понимать, что для переменных нет значений по умолчанию и что присвоение None
чему-либо - это не то же самое, что удаление имени с помощью del
. Вам нужно будет явно определить запасной вариант, например:
def example_if():
result = 1 if random.random() < 0.5 else 0
return result
(Эта ошибка также может быть признаком логической ошибки с отступом: проверьте, также предполагается, что код, использующий переменную, находится внутри if
блока.)
Скрытым вариантом этого является цикл, который должен присваивать значение, но выполняется ноль раз:
def example_for():
for i in range(0): # this loop will not be entered
result = 1 # therefore this assignment will not happen
return result
print(example_for()))
В более практических примерах for
цикл может не выполняться, потому что он выполняет итерацию по результатам операции поиска (например, другого цикла), которая ничего не нашла, или потому, что он выполняет итерацию по файлу, который уже был прочитан.
UnboundLocalError
это также может произойти при попытке изменить переменную, которая еще не была назначена:
def example_increment():
result += 1
example_increment()
Это происходит потому, что необходимо просмотреть существующее значение, прежде чем его можно будет изменить, но искать нечего.
В любом случае ошибка произойдет, даже если в глобальной области видимости есть result
, потому что Python заранее решил рассматривать переменную как локальную. Чтобы решить эту проблему, используйте global
ключевое слово в дополнение к заданию начального глобального значения. Это должно использоваться в каждой функции, которая хочет изменить глобальную. (global
в глобальной области видимости не имеет никакого эффекта.)
import random
result = 1
def example_increment():
global result
result += 1
def example_if():
global result
if random.random() < 0.5:
result = 1
return result
example_increment() # result becomes 2
print(example_if()) # may or may not reset to 1
Использование global
не обязательно, если значение доступно только для чтения. Однако глобальное значение нельзя использовать для установки начального значения для локального с тем же именем:
result = 1
def does_not_work():
result = result # trying to set a "default" value for a local
if random.random() < 0.5:
result = 2
return result
Это не может работать в любом случае. Как есть, UnboundLocalError
произойдет. Если global result
добавляется в функцию, то result = result
не будет иметь никакого эффекта, и функция потенциально изменит глобальную (здесь не требуется). В этих случаях обходным путем является простое использование другого имени для локального.