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]

 


Зв’язані теми