NameError: name 'total' is not defined означает, что Python не нашёл имя total ни в одной доступной области видимости. UnboundLocalError: cannot access local variable 'total' where it is not associated with a value точнее: имя уже считается локальным в функции, но до чтения не получило значения.
UnboundLocalError — подкласс NameError, поэтому обработчик except NameError поймает обе ошибки. Для диагностики это разные сигналы: при NameError ищите опечатку, порядок выполнения или неверный импорт; при UnboundLocalError — присваивание внутри текущей функции. Примеры ниже проверены на CPython 3.14.5.
Имя ищется от ближайшей области к дальней
NameError.Чем NameError отличается от UnboundLocalError
| Сообщение | Что уже известно | Частая причина | Первая проверка |
|---|---|---|---|
NameError: name 'user_name' is not defined | Имя не найдено по LEGB | Опечатка, регистр, ранний вызов, неверная форма импорта | Сравнить имя символ в символ и найти, где оно должно связываться |
UnboundLocalError: cannot access local variable 'discount' where it is not associated with a value | Компилятор уже отнёс discount к локальным именам функции | Присваивание есть ниже по коду или в невыполненной ветке | Найти все discount = ..., discount += ..., цели for, with, except и импорты внутри этой функции |
Проверить иерархию можно без догадок:
print(issubclass(UnboundLocalError, NameError))
True
Как Python ищет имя по LEGB
Мнемоника LEGB описывает порядок для обычного чтения имени в функции:
- Local — локальная область текущей функции: её параметры и имена, связанные в теле.
- Enclosing — локальные области объемлющих функций, от ближайшей к дальней.
- Global — пространство имён модуля.
- Builtins — встроенные имена вроде
len,printиNameError.
rate = 0.1
def make_price():
rate = 0.2
def price(net):
return net * (1 + rate)
return price
price = make_price()
print(price(1000))
1200.0
rate нет в price, но он найден в ближайшей объемлющей функции make_price. Поиск не доходит до глобального rate = 0.1.
if, for, while, try и with не создают отдельных локальных областей. Имя, связанное в ветке if, остаётся локальным именем всей функции. Но значение у него появится, только если само присваивание выполнилось.
Почему присваивание делает имя локальным во всей функции
Компилятор просматривает весь блок функции до её запуска. Если в блоке есть операция связывания имени и нет global или nonlocal, все употребления этого имени в блоке считаются локальными. Неважно, стоит присваивание до чтения, после него или в ветке, которая не выполнилась.
Здесь discount = 0.2 делает discount локальным даже при vip=False:
discount = 0.1
def final_price(total, vip):
if vip:
discount = 0.2
return total * (1 - discount)
final_price(1000, False)
UnboundLocalError: cannot access local variable 'discount' where it is not associated with a value
Значение должно появиться на всех путях. Здесь достаточно вычислить локальное значение один раз:
def final_price(total, vip):
discount = 0.2 if vip else 0.1
return total * (1 - discount)
print(final_price(1000, False))
print(final_price(1000, True))
900.0
800.0
Чтение выше присваивания падает по той же причине:
total = 0
def add_order():
print(total)
total += 1
add_order()
total += 1 сначала читает total, затем записывает новое значение. Сама запись делает имя локальным, поэтому первый print(total) уже не может взять глобальный total.
Опечатка, регистр, порядок определения и import
Python различает регистр: user_name, User_Name и USER_NAME — три разных имени. При опечатке CPython иногда добавляет подсказку Did you mean, но наличие и выбор кандидата не гарантированы. Сверяйте имя с местом определения.
Операторы def, class и import связывают имена только при выполнении соответствующей строки. Функции не «поднимаются» в начало файла:
print(format_name("Ada"))
def format_name(name):
return name.upper()
Перенесите вызов после def или вызывайте его из main(), запущенной в конце файла.
Форма импорта тоже определяет, какое имя появится:
import math
print(math.sqrt(81))
9.0
После import math доступно имя math, но не sqrt. Для вызова sqrt(81) нужен from math import sqrt. Если Python не нашёл сам модуль, это уже ModuleNotFoundError, а не NameError.
Замыкания и nonlocal
Замыкание — функция, которая сохраняет доступ к именам объемлющей функции даже после её завершения. Читать такое имя можно без nonlocal. Директива нужна, когда внутренняя функция должна перепривязать имя, а не просто изменить объект через ссылку.
Без nonlocal count строка count += 1 сделает count локальным именем next_value и сразу попытается прочитать его до первого значения. С nonlocal перепривязывается count из ближайшей внешней функции:
def make_counter():
count = 0
def next_value():
nonlocal count
count += 1
return count
return next_value
counter = make_counter()
print(counter())
print(counter())
1
2
nonlocal ищет уже связанное имя только в объемлющих функциях. Он не может создать новое имя или ссылаться на глобальное. Если подходящего связывания нет, код не скомпилируется: SyntaxError: no binding for nonlocal 'count' found.
Замыкания часто встречаются в декораторах: обёртка помнит параметры и локальные имена функции-фабрики.
Когда global уместен, а когда маскирует проблему
global total направляет все обращения к total в текущем блоке в пространство имён модуля. Для обычного чтения глобального имени директива не нужна; она нужна для перепривязки:
total = 0
def add_order():
global total
total += 1
add_order()
print(total)
1
Это технически исправляет UnboundLocalError, но завязывает функцию на скрытое изменяемое состояние. Два теста или два одновременных вызова могут влиять друг на друга. Обычно проще передать значение параметром и вернуть новое:
def add_order(total):
return total + 1
total = add_order(0)
print(total)
1
global уместен для осознанного модульного состояния. nonlocal уместен для замкнутого состояния небольшой функции. Оба слова работают на весь текущий блок и должны стоять до первого использования имени. Ни одно из них не стоит добавлять как универсальный фикс к любому UnboundLocalError.
Почему class body не обычный enclosing scope
При выполнении class Python создаёт отдельное пространство имён. Присваивания в теле класса попадают в него, а после завершения становятся атрибутами класса. Но это пространство не становится обычным Enclosing-уровнем для методов.
class Invoice:
tax_rate = 0.2
def total(self, net):
return net * (1 + tax_rate)
Invoice().total(1000)
Метод не найдёт tax_rate и получит NameError. Обращайтесь к атрибуту явно:
class Invoice:
tax_rate = 0.2
def total(self, net):
return net * (1 + self.tax_rate)
print(Invoice().total(1000))
1200.0
self.tax_rate подходит, если экземпляру разрешено переопределить атрибут. Если нужно значение именно текущего класса, пишите type(self).tax_rate; для конкретного класса — Invoice.tax_rate.
Область видимости comprehension в Python 3
Целевая переменная list, set или dict comprehension живёт в отдельной неявной области. В Python 3 она не «протекает» наружу:
squares = [number**2 for number in range(3)]
print(squares)
print(number)
[0, 1, 4]
NameError: name 'number' is not defined
Это защита от случайной перезаписи внешнего имени, а не ошибка comprehension. Если number нужен после, возьмите его из исходных данных или верните нужное значение явно. Синтаксис и порядок вычисления разобраны в статье про list comprehension.
Переменная исключения удаляется после except
Имя из except ... as exc доступно внутри обработчика, но Python явно очищает его при выходе из блока. Это разрывает цикл ссылок между исключением, traceback и кадром функции.
try:
int("not a number")
except ValueError as exc:
print(type(exc).__name__)
print(exc)
Первый print выведет ValueError, второй получит NameError: name 'exc' is not defined. Если текст нужен после обработчика, скопируйте его в другое имя:
error_message = None
try:
int("not a number")
except ValueError as exc:
error_message = str(exc)
print(error_message)
invalid literal for int() with base 10: 'not a number'
Общая схема try, нескольких обработчиков и finally есть в руководстве по try/except.
Безопасные исправления
Исправляйте не последнюю строку traceback, а поток данных, из-за которого у имени не оказалось значения.
- Параметр явно передаёт зависимость в функцию. Это обычно лучше скрытого глобального имени.
returnвозвращает новое значение вместо неявной перезаписи внешнего имени.- Инициализация до ветвления гарантирует, что локальное имя связано на каждом пути. Запасное значение должно иметь доменный смысл, а не просто прятать ошибку.
- Объект состояния хранит несколько связанных полей и методов. Он уместен, когда одного скаляра и
returnуже мало.
Например, счётчик с двумя операциями проще хранить в объекте, чем в наборе глобальных переменных:
class OrderStats:
def __init__(self):
self.accepted = 0
self.rejected = 0
def record(self, is_valid):
if is_valid:
self.accepted += 1
else:
self.rejected += 1
stats = OrderStats()
stats.record(True)
stats.record(False)
print(stats.accepted, stats.rejected)
1 1
Короткий порядок диагностики
- Прочитайте последнюю строку traceback и выпишите точное имя.
- Различите
NameErrorиUnboundLocalError; во втором случае сразу ищите связывания во всей функции. - Для
NameErrorпройдите LEGB: текущая функция, объемлющие функции, модуль, встроенные имена. - Проверьте регистр, порядок
defиimport, а также форму импорта. - Проверьте все пути управления: ветка могла не выполниться, цикл — не сделать ни одной итерации, а обработчик — не запуститься.
- Выберите исправление, которое делает поток данных явным: параметр и
return, инициализация до ветвления, замыкание сnonlocalили объект состояния.
global и nonlocal не «объявляют» любую переменную в нужном месте. Они меняют, как компилятор связывает имя: с модулем или с ближайшей объемлющей функцией. Если функция просто получает значение и возвращает результат, ей не нужна ни одна из этих директив.