Python. Ключевое слово nonlocal. Особенности использования. Примеры




Ключевое слово nonlocal. Особенности использования. Примеры

Перед использованием данной темы рекомендуется ознакомиться со следующей темой:


Содержание


Поиск на других ресурсах:

1. Для чего предназначена инструкция nonlocal? Общая форма использования

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

Использование этого механизма дает возможность поддерживать доступную к изменению информацию о состоянии, которое восстанавливается при последующих вызовах вложенной функции.

Общая форма использования инструкции nonlocal

def Fn():
    nonlocal name1, name2, ..., nameN

здесь

  • Fn() – функция, которая есть вложенной в некоторой другой функции;
  • name1, name2, nameN – имена, которые существуют в области видимости объемлющей функции. Функция Fn() есть вложенной в другую объемлющую функцию.

В случае простого вложения функции FnIn() в пределах объемлющей функции FnOut() общая форма объявления инструкции nonlocal следующая:

def FnOut():
    ...
    def FnIn():
        nonlocal name1, name2, ..., nameN
...

здесь

  • FnOut() – объемлющая функция, в которой объявляется вложенная функция FnIn();
  • FnIn() – вложенная функция, которая содержит инструкцию nonlocal;
  • name1, name2, nameN – имена, которые объявлены в объемлющей функции FnOut() и используются во вложенной функции FnIn().

 

2. Особенности применения инструкции nonlocal. Отличие между инструкциями global и nonlocal

Инструкция nonlocal применяется во вложенных функциях если нужно изменять переменные, которые были объявлены в объемлющих функциях.

Отличие между инструкциями nonlocal и global следующее:

  • инструкция global позволяет изменять значения переменных в глобальной области видимости модуля;
  • инструкция nonlocal изменяет значение переменных в объемлющей функции. Для глобальной области видимости инструкция nonlocal не имеет действия.

 

3. Особенности применения инструкции nonlocal в случае, когда используется несколько уровней вложенных функций. Пример

Пример. Пусть задано несколько вложенных функций. Внешняя функция Fn1() содержит вложенную функцию Fn2(). Вложенная функция Fn2() содержит другую вложенную функцию Fn3(). В функциях Fn1() и Fn2() объявляется переменная с именем x1, которой присваивается некоторое значение. В функции Fn3() переменная x1 изменяется с помощью инструкции nonlocal. В результате будет изменяться переменная x1 ближайшей объемлющей функции, в нашем случае – функции Fn2().

# Инструкция nonlocal

# Несколько объемлющих функций

def Fn1():
    # эта переменная не изменяется из функции Fn3(),
    # потому что ее перекрывает переменная из функции Fn2()
    x1 = 25

    def Fn2():
        x1 = 33 # эта переменная будет изменяться в функции Fn3()

        def Fn3():
            nonlocal x1
            x1 = 55 # Fn2.x1 = 55

        Fn3()
        print('Fn2.x1 = ', x1)

    Fn2()
    print('Fn1.x1 = ', x1)

Fn1()

Результат выполнения программы

Fn2.x1 = 55
Fn1.x1 = 25

Если в теле функции Fn2() убрать строку x1 = 33, то изменяться будет значение переменной x1 функции высшего уровня, то есть функции Fn1(). В этом случае программа выдаст результат

Fn2.x1 = 55
Fn1.x1 = 55

 

4. Требования к объемлющим функциям, которые содержат вложенные функции с инструкциями nonlocal

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

  • программа содержала код вложенных функций. Одна из функций считается объемлющей, другая – вложенной;
  • в объемлющей функции была объявлена переменная, которая изменяется во вложенной функции. Эта переменная должна быть объявлена перед объявлением вложенной функции.

 

5. Отличия в правилах поиска имен интерпретатором в инструкциях global и nonlocal

Если во вложенной функции используется инструкция nonlocal, то интерпретатор выполняет следующие действия:

  • пропускает локальную область видимости функции, в которой объявлена инструкция nonlocal;
  • осуществляет поиск в областях видимости объемлющих функций начиная с ближайшей объемлющей функции.

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

 

6. Примеры использования инструкции nonlocal

Пример 1. В примере продемонстрирован доступ к переменной out объемлющей функции FnOut() из функции FnIn().

# Инструкция nonlocal

# Объемлющая функция FnOut()
def FnOut():

    out = 25 # объявить переменную out

    # Вложенная функция, в которой используется инструкция nonlocal
    def FnIn():

        # доступ к переменной out объемлющей функции FnOut()
        nonlocal out
        out = 777;
        return

    # Вызов вложенной функции FnIn() из тела функции FnOut()
    FnIn()

# Вывод переменной out
print('FnOut.out = ', out)

# Глобальная область видимости.
# Вызов функции FnOut()
FnOut() # будет выведено: FnOut.out = 777

Результат выполнения программы

FnOut.out = 777

Как видно из результата, переменная out из функции FnOut() была изменена в теле функции FnIn() благодаря инструкции nonlocal.

Пример 2. Решение квадратного уравнения. В примере демонстрируется доступ к переменным x1, x2 объемлющей функции SquareEquition() из вложенной функции Calc().

# Инструкция nonlocal. Решение квадратного уравнения

# Подключить математический модуль для того, чтобы
# использовать функцию math.sqrt()
import math

# Объявить две функции: объемлющую SquareEquition() и вложенную Calc()
def SquareEquition(a, b, c):

    # В начале принять, что уравнение не имеет корней
    x1 = None
    x2 = None

    # Вложенная функция Calc() - заполняет значения x1, x2
    def Calc(a, b, c):
        D = b*b-4*a*c

        if (D>=0):
            # доступ к x1, x2 объемлющей функции SquareEquition()
            nonlocal x1, x2 # объявить 2 имени в инструкции nonlocal
            x1 = (-b - math.sqrt(D))/(2.0*a)
            x2 = (-b + math.sqrt(D))/(2.0*a)

    Calc(a, b, c)
    return [x1, x2]

# Использование функции SquareEquition(),
# ввод данных
print("Input a, b, c:")
a = float(input('a = '))
b = float(input('b = '))
c = float(input('c = '))
roots = SquareEquition(a, b, c)
print('roots = ', roots)

Результат работы программы

Input a, b, c:
a = 1
b = 1
c = -2
roots = [-2.0, 1.0]

 

7. Какие ограничения накладываются на инструкцию nonlocal?

На инструкцию nonlocal накладываются следующие ограничения:

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

Пример. Если попробовать выполнить следующий пример

# Ограничения, которые накладываются на nonlocal

D = 'Hello' # Объявить глобальное имя

def FnOut():
    def FnIn():
        # Попытка связать имя D с глобальным именем D
        nonlocal D
        D = 'world!'

    # Вызвать функцию FnIn() из тела функции FnOut()
    FnIn()
    print('FnIn.D = ', D)

# Вызвать функцию FnOut() из области видимости модуля
FnOut()

то интерпретатор выдаст ошибку:

SyntaxError: no binding for nonlocal 'D' found

Это означает, что имя D должно быть предварительно объявлено в объемлющей функции FnOut(), а не в глобальной области видимости. Например функция FnOut() может быть изменена следующим образом

...

def FnOut():
    D = 'Hello'

    def FnIn():
        # имя D связывается с именем D объемлющей функции
        nonlocal D
        D = 'world!'

    # Вызвать функцию FnIn() из тела функции FnOut()
    FnIn()
    print('FnIn.D = ', D)

...

После этого программа заработает и выдаст результат

FnIn.D = world!

 

8. Пример, в котором в инструкции nonlocal объявляется несколько имен

 

# Сложение комплексных чисел (a1+b1*j)+(a1+b2*j)

def ComplexAdd(a1, b1, a2, b2):

    a3 = 0
    b3 = 0

    def Add():
        nonlocal a3, b3 # Использование двух имен в инструкции nonlocal
        a3 = a1+a2
        b3 = b1+b2

    Add()
    return [a3, b3]

result = ComplexAdd(2, 3, -1, 4)
print('result = ', result) # result =   [1, 7]

 


Связанные темы