Сообщить об ошибке.

Разрешение имен и область видимости в программе Python

Область видимости определяет видимость имени переменной в блоке кода. Определение переменной в коде происходит в результате операций связывания или присваивания.

Если переменная определена в блоке кода, ее область действия включает этот блок. Если определение встречается в функциональном блоке, область действия расширяется на любые блоки кода, входящие в этот блок, если только содержащий блок не вводит другую привязку для имени (nonlocal или global).

Если переменная используется в блоке кода, то имя переменной разрешается с использованием ближайшей заключающей ее области. Набор всех таких областей, видимых для блока кода, называется средой блока environment.

Если имя переменной не найдено вообще, возникает исключение NameError. Если текущая область является областью определения функции, а имя ссылается на локальную переменную, которая еще не была привязана к значению, то возникает исключение UnboundLocalError. UnboundLocalError - это подкласс NameError.

Если определение переменной происходит где-либо в блоке кода, все случаи использования имени в блоке рассматриваются как ссылки на текущий блок. Это может привести к ошибкам, когда имя используется внутри блока до его привязки. Это правило неуловимо. В Python отсутствует объявления переменных и разрешены операций связывания и присваивания имен переменных в любом месте блока кода. Локальные переменные блока кода могут быть определены путем сканирования всего текста этого блока на предмет операций связывания и присваивания имен.

Пространство имен для модуля создается автоматически при первом импорте import модуля. Основной модуль для скрипта всегда называется __main__.

Ключевые слова global и nonlocal.

Если оператор global встречается внутри блока, все случаи использования имени переменной, указанной в этом операторе, относятся к связыванию этого имени в пространстве имен верхнего уровня. Имена разрешаются в пространстве имен верхнего уровня путем поиска глобального пространства имен, т. е. пространства имен файла сценария (модуля) содержащего код и пространства имен встроенных модулей builtins. Сначала выполняется поиск в глобальном пространстве имен. Если имя не найдено, выполняется поиск в пространстве имен встроенных модулей builtins. Инструкция global должна предшествовать всем видам использования имени переменной в блоке кода.

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

Инструкция nonlocal заставляет соответствующее имя ссылаться на ранее связанные переменные в ближайшей области действия заключающей функции. Во время компиляции будет вызываться исключение SyntaxError, если данное имя не существует ни в одной области действия заключающей функции.

x = 10

def gglobal():
    global x
    x = 20

def outer():
    y = 5
    
    def enclosing():
        nonlocal y
        y = 15
    
    enclosing()
    print(f'{y=}')

gglobal()
outer()
print(f'{x=}')

# ВЫВОД скрипта:
# y=15  (изменена в `enclosing()`)
# x=20   (изменена в `gglobal()`)

Ключевое слово global сообщает Python, что имеется в виду global x, а nonlocal указывает, что ориентируемся на y из охватывающей функции.

Разрешения имен в классах

Блоки определения классов и аргументы для exec() и eval() являются особыми в контексте разрешения имен. Определение класса - это исполняемый оператор, который может использовать и присваивать имена. Эти ссылки следуют обычным правилам разрешения имен, за исключением того, что несвязанные локальные переменные ищутся в глобальном пространстве имен. Пространство имен определения класса становится словарем атрибутов класса. Область видимости имен, определенных в блоке класса, ограничена блоком этого класса. Область видимости класса не распространяется на блоки методов, к которым относятся короткие выражения для создания списков, кортежей, словарей и множеств, а так же выражения-генераторы, поскольку они реализуются с использованием области действия функции. Это означает, что следующее не удастся:

class A:
    a = 42
    b = list(a + i for i in range(10))

Дополнительно смотрите материал "Пространство имен и область видимости в классах"

Обобщение ранее сказанного

Типы пространств имен

Python имеет различные пространства имен, создаваемые и удаляемые в разное время:

  • Встроенное пространство имен (built-in): содержит встроенные функции и исключения Python. Создается при запуске интерпретатора Python.
  • Глобальное пространство имен (global) : специфичное для модуля или скрипта. Создается при импорте модуля или запуске скрипта.
  • Заключающее пространство имен (enclosing): существует для вложенных функций. Оно связывает несколько пространств имен функций от самого внутреннего до самого внешнего.
  • Локальное пространство имен (local): создается при вызове функции. Как только выполнение функции завершается, пространство имен отбрасывается.

Область действия переменных

Область действия определяет область кода, в которой к переменной можно получить доступ или изменить ее. В Python есть четыре основных области действия переменных:

  • Local (L): Внутри текущей функции.
  • Enclosing (E): Внутри заключающих функций.
  • Global (G): На верхнем уровне модуля.
  • Built-in (B): Во встроенном пространстве имен.

Эти области образуют правило LEGB, которому Python следует при разрешении имен переменных.

Понимание области видимости в примерах

Типы пространств имен

x = 10  # global variable 
def outer():
    y = 5  # enclosing variable     
    def inner():
        z = 3 # local var         
        print(x, y, z)
    inner()

outer()

Когда вызывается функция inner(), то она обращается к:

  • z из его локальной области видимости.
  • y из охватывающей области outer_function().
  • x из глобальной области видимости.

Избегайте затенения переменных

Если локальная переменная имеет то же имя, что и глобальная переменная или встроенная, она затеняет глобальную или встроенную переменную:

x = 10

def shadow():
    x = 5
    print(x)

shadow()  
# 5 

print(x)
# 10  (глобальная переменная `x` не изменилась)

Затенение может привести к неожиданному поведению, поэтому рекомендуется избегать использования одних и тех же имен в разных областях.