Область видимости в Python

Вы, наверное, не раз слышали термин область видимости, когда изучали программирование. Это весьма важная тема, незнание которой может привести к достаточно запутанным ошибкам. Область видимости указывает интерпретатору, когда наименование (или переменная) видимо. Другими словами, область видимости определяет, когда и где вы можете использовать свои переменные, функции, и т.д. Если вы попытаетесь использовать что-либо, что не является в вашей области видимости, вы получите ошибку NameError.

Python содержит три разных типа области видимости:

  • Локальная область видимости
  • Глобальная область видимости
  • Нелокальная область видимости (была добавлена в Python 3)

(adsbygoogle = window.adsbygoogle || []).push({});

Локальная область видимости

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

Pythonx = 10def my_func(a, b): print(x) print(z)my_func(1, 2)1234567x = 10 def my_func(a, b):    print(x)    print(z) my_func(1, 2)

Результат

Python10Traceback (most recent call last): File «<pyshell#19>», line 1, in <module> my_func(1, 2) File «<pyshell#18>», line 3, in my_func print(z)NameError: name ‘z’ is not defined123456710Traceback (most recent call last):    File «<pyshell#19>», line 1, in <module>        my_func(1, 2)    File «<pyshell#18>», line 3, in my_func        print(z)NameError: name ‘z’ is not defined

Здесь мы создаем переменную х и очень простую функцию, которая принимает два аргумента. Далее она выводит х и z. Обратите внимание на то, что мы не определили z, так что когда мы вызываем функцию, мы получаем ошибку NameError. Это происходит в связи с тем, что z не определена, или находится вне области видимости. Если вы определите z перед вызовом функции, тогда она будет найдена и ошибка NameError не возникнет. Ошибка NameError также возникает при попытке получения доступа к переменной, которая находится только внутри функции:

Pythondef my_func(a, b): i = 2 print(x)if __name__ == ‘__main__’: x = 10 my_func(1, 2) print(i)123456789def my_func(a, b):    i = 2    print(x)  if __name__ == ‘__main__’:    x = 10    my_func(1, 2)    print(i)

Переменная i определена только внутри функции, так что при запуске кода мы получаем ошибку NameError. Давайте немного модифицируем первый пример. Разместим данный код в python-файл и попытаемся его запустить:

Pythondef my_func(a, b): x = 5 print(x)if __name__ == ‘__main__’: x = 10 my_func(1, 2) print(x)123456789def my_func(a, b):    x = 5    print(x)  if __name__ == ‘__main__’:    x = 10    my_func(1, 2)    print(x)

  1. Что, по-вашему, должно произойти?
  2. Выдаст ли код цифру 10 дважды?

Нет, не выдаст. Причина в том, что мы имеем две переменные х. Переменная х внутри my_func имеет локальную область видимости функции и переопределяет переменную х вне функции. Так что когда мы вызываем функцию my_func, в выдаче мы видим 5, а не 10. Затем, когда функция возвращается, переменная х внутри функции my_func является кучей мусора и область для выдачи х срабатывает еще один раз. По этой причине последний оператор выдачи выдает именно 10. Если вы хотите кое-что поинтереснее, вы можете попытаться вывести х перед тем как назначить его в нашей функции:

Pythondef my_func(a, b): print(x) x = 5 print(x)if __name__ == ‘__main__’: x = 10 my_func(1, 2) print(x)12345678910def my_func(a, b):    print(x)    x = 5    print(x)  if __name__ == ‘__main__’:    x = 10    my_func(1, 2)    print(x)

Кода вы запустите этот код, вы получите ошибку:

PythonUnboundLocalError: local variable ‘x’ referenced before assignment1UnboundLocalError: local variable ‘x’ referenced before assignment

Это происходит потому, что Python замечает, что вы назначаете х в функцию my_func позже, что и приводит к ошибке, так как х еще не определен.

Глобальная область видимости

Python содержит оператор global. Это ключевое слово Python. Оператор global объявляет переменную доступной
для блока кода, следующим за оператором. Хотя вы и можете создать наименование, перед тем, как объявить его глобальным, я настоятельно не рекомендую этого делать. Давайте попробуем использовать глобальную область для исправления нашей ошибке из предыдущего примера:

Pythondef my_func(a, b): global x print(x) x = 5 print(x)if __name__ == ‘__main__’: x = 10 my_func(1, 2) print(x)1234567891011def my_func(a, b):    global x    print(x)    x = 5    print(x)  if __name__ == ‘__main__’:    x = 10    my_func(1, 2)    print(x)

Выдача данного кода должна быть следующей:

Python10551231055

Объявляя х как глобальный, мы говорим Python использовать первое объявление х для нашего первого оператора вывода в функции. Далее мы даем х новое значение – а именно 5, и выводим его еще раз, перед тем как выйти из функции. Обратите внимание на то, что х становится глобальной переменной, когда мы доходим до последнего оператора вывода в конце кода, и там х все еще равен 5. Давайте смешаем глобальную и локальную область видимости и поглядим, что из этого получится:

Pythondef my_func(a, b): global c b, a = a, b d = ‘Mike’ print(a, b, c, d)a, b, c, d = 1, 2, ‘c is global’, 4my_func(1, 2)print(a, b, c, d)12345678910def my_func(a, b):    global c    b, a = a, b    d = ‘Mike’    print(a, b, c, d)  a, b, c, d = 1, 2, ‘c is global’, 4my_func(1, 2)print(a, b, c, d)

Здесь мы назначаем с в качестве глобальной переменной. Таким образом, «с» будет выводиться и внутри и снаружи нашей функции. Мы также меняем местами значения переменных а и b в функции, чтобы показать, что мы можем переназначить их внутри функции, не меняя их вне функции. Это показывает, что переменные а и b не являются глобальными. Если вы запустите этот код, вы увидите следующую выдачу:

Python2 1 c is global Mike1 2 c is global 4122 1 c is global Mike1 2 c is global 4

Я хочу обратить ваше внимание на то, что не нужно менять глобальные переменные внутри функции. Комьюнити Python объявило такую практику очень нежелательной, так как из-за этого исправление кода становится намного сложнее. Теперь, с пониманием основных принципов локальных и глобальных областей, мы можем перейти к нелокальной области non_local.

Область nonlocal

В Python 3 было добавлено новое ключевое слово под названием nonlocal. С его помощью мы можем добавлять переопределение области во внутреннюю область. Вы можете ознакомиться со всей необходимой на данный счет информацией в PEP 3104. Это наглядно демонстрируется в нескольких примерах. Один из самых простых – это создание функции, которая может увеличиваться:

Pythondef counter(): num = 0 def incrementer(): num += 1 return num return incrementer123456def counter():    num = 0    def incrementer():        num += 1        return num    return incrementer

Если вы попробуете запустить этот код, вы получите ошибку UnboundLocalError, так как переменная num ссылается прежде, чем она будет назначена в самой внутренней функции. Давайте добавим nonlocal в наш код:

Pythondef counter(): num = 0 def incrementer(): nonlocal num num += 1 return num return incrementer1234567def counter():    num = 0    def incrementer():        nonlocal num        num += 1        return num    return incrementer

Результат работы:

Pythonc = counter()print(c) # <function counter.<locals>.incrementer at 0x7f45caf44048>c() # 1c() # 2c() # 3123456c = counter()print(c) # <function counter.<locals>.incrementer at 0x7f45caf44048> c() # 1c() # 2c() # 3

Теперь наша возрастающая функция работает именно так, как мы от нее и ожидаем. Для заметки, тип такой функции называется closure (дословно – закрытие). Такая функция является блоком кода, который «закрывает» переменные nonlocal. Суть в том, что вы можете ссылать переменные, которые определены вне вашей функции. Обычно, nonlocal позволяет вам назначать переменные во внешней области, но не в глобальной. Так что вы не можете использовать nonlocal в функции подсчета, так как в таком случае она попытается передать функцию глобальной области. Попробуйте, и получите ошибку SyntaxError незамедлительно. Вместо этого, вам нужно использовать nonlocal во вложенной функции.

Подведем итоги

В данной статье мы смогли понять то, как переменная ссылается, используя ключевые слова Python global и nonlocal. Мы узнали, где мы можем их использовать и зачем. Мы также узнали кое-что о локальной области видимости. Хорошего дня и продолжайте экспериментировать!