Области видимости переменных. Ключевые слова global и nonlocal

Курс по Python: https://stepik.org/course/100707

На этом занятии мы поговорим о работе с глобальными и локальными данными, то есть, фактически, об области видимости переменных.

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

myprog.py

то в Python автоматически создается пространство имен с названием myprog (слайд). Изначально оно пустое, но если там задать какие-либо переменные:

N = 100
WIDTH, HEIGHT = 1000, 500

то в этом пространстве появятся эти имена (ссылки). Так вот, все переменные, которые находятся непосредственно в этом пространстве имен, являются глобальными в пределах текущего программного модуля. То есть, они доступны в любом месте данной программы, после их объявления.

Добавим в эту программу еще и функцию, пусть она будет такой:

def my_func(lst):
    for x in lst:
        n = x + 1
        print(n)

В этом случае в пространстве имен появится переменная my_func – ссылка на объект-функцию, причем эта функция образует свое, локальное пространство имен с названием my_func. И внутри этого локального пространства автоматически появляются переменные lst, x и n. Обратите внимание, цикл for не образовывает своего локального пространства, только функция и все переменные внутри цикла находятся в области видимости функции.

Как вы уже догадались, переменные внутри локальной области видимости, называются локальными и доступны только в ее пределах. Например, вне функции мы не можем обращаться к локальным переменным lst, x и n. Строчка кода:

print(x)

приведет к ошибке – x не определена. А вот внутри функции все три переменные существуют:

print(lst, n, x)

Если теперь вызвать функцию:

my_func([1, 2, 3])

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

print(N, WIDTH)

то значения этих переменных будут отображены.

А что будет, если мы внутри функции определим локальную переменную N, то есть, с тем же именем, что и глобальная переменная? Давайте попробуем:

def my_func(lst):
    N = 10
    …

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

my_func([1, 2, 3])
print(N)

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

def my_func(lst):
    global N
    …

Теперь имя N – это имя глобальной переменной и ее изменение внутри функции приведет к изменению и глобального значения.

Однако, здесь следует быть аккуратным, если в функции уже была создана локальная переменная N, то конструкция global приведет к ошибке:

    N = 5
    global N

Так можно делать только в отсутствии соответствующих локальных переменных.

Интересно, что если сейчас глобальную переменную N поставить в комментарий:

# N = 100

то есть, она не будет существовать изначально в программе, то после выполнения функции с конструкцией global N, эта глобальная переменная будет создана.

В Python имеется еще один интересный режим работы с локальными переменными с использованием ключевого слова nonlocal. Давайте предположим, что у нас имеется объявление одной функции внутри другой (так тоже можно делать):

x = 0
def outer():
    x = 1
    def inner():
        x = 2
        print("inner:", x)
 
    inner()
    print("outer:", x)
 
outer()
print("global:", x)

При запуске программы мы увидим ожидаемые результаты, так как берутся локальные переменные внутри каждой функции:

inner: 2
outer: 1
global: 0

А теперь представим, что внутри функции inner мы хотели бы работать с переменной x, объявленной уровнем выше, то есть, в функции outer. Для этого в локальной области этой функции следует прописать ключевое слово nonlocal и указать переменную, которую следует взять из внешней области видимости:

nonlocal x

Теперь строка x = 2 будет означать изменение переменной x в функции outer и при запуске программы получим результаты:

inner: 2
outer: 2
global: 0

Но так можно делать только с локальными переменными. С глобальной работать не будет. Если мы пропишем строчку

nonlocal x

в функции outer, то возникнет ошибка, т.к. уровнем выше находится уже глобальная область. Здесь, вместо nonlocal следует уже использовать global:

global x

а nonlocal в inner убрать, иначе опять же получится ссылка на глобальную переменную.

Теперь вы знаете, что такое глобальные и локальные переменные, как к ним обращаться и как использовать ключевые слова global и nonlocal. Для закрепления материала вас ждут практические задания, а я ухожу в следующий урок.

Курс по Python: https://stepik.org/course/100707

Видео по теме