Python. Вкладені функції. Вкладені області видимості. Правила пошуку імен у випадку вкладених функцій. Фабричні функції. Передача значень у вкладену функцію

Вкладені функції. Вкладені області видимості. Правила пошуку імен у випадку вкладених функцій. Фабричні функції. Передача значень у вкладену функцію


Зміст


Пошук на інших ресурсах:




1. Що таке вкладені функції?

У мові програмування Python можуть використовуватись вкладені функції. Це означає, що всередині інструкції def може бути інша інструкція def. Кількість вкладень функцій довільна. В найбільш загальному випадку форма оголошення вкладених функцій наступна:

def Fn_1(parameters1):
    ...
    def Fn_2(parameters2):
        ...
        def Fn_N(parametersN):
            ...
            return
        ...
        return
return

тут

  • Fn_1, Fn_2, Fn_N – імена функцій, що відповідно отримують параметри parameters1, parameters2, parametersN.

 

2. Правила пошуку імен всередині функції у випадку вкладених областей видимості

Якщо всередині функції, яка є вкладеною в іншу функцію, оголошено ім’я (змінну) то діють наступні правила:

1. У випадку звертання до імені пошук цього імені здійснюється в наступній послідовності:

  • пошук в локальній області видимості;
  • пошук в усіх охоплюючих функціях в порядку зсередини назовні;
  • пошук в поточній глобальній області видимості (пошук в поточному модулі);
  • пошук у вбудованій області видимості (модуль buildins).

2. Якщо всередині функції оголошено глобальне ім’я з допомогою інструкції global, то пошук імені починається одразу з глобальної області видимості.

3. Якщо до деякого об’єкту використана операція присвоювання за замовчуванням (t = value), то ця операція:

  • створює нове ім’я в локальній області видимості;
  • змінює ім’я в поточній локальній області видимості.

4. Якщо всередині функції визначено глобальное ім’я (з допомогою ключового слова global), то операція присвоювання:

  • створює нове ім’я в області видимості охоплюючого модуля;
  • змінює ім’я, яке було оголошене в охоплюючому модулі.

5. Якщо всередині функції визначене нелокальне ім’я (інструкція nonlocal), то операція присвоювання:

  • створює нове ім’я в найближчій охоплюючій функції;
  • змінює ім’я, яке було створене в найближчій охоплюючій функції.

 

3. Приклади вкладених функцій

Приклад 1. Дано функцію SquareEquation(), яка обчислює розв’язки квадратного рівняння. Всередині функції міститься вкладена функція Disc(), яка обчислює дискримінант.

# Вкладені функції

import math

# Функція розв'язку квадратного рівняння,
# дана функція є охоплюючою для вкладеної функції Disc().
# Функція повертає розв'язок рівняння у вигляді списку
def SquareEquation(a, b, c):

    # Вкладена функція Disc() обчислення дискримінанту
    def Disc(a, b, c):
        d = b*b-4*a*c
        return d

    # Обчислити дискримінант
    D = Disc(a, b, c)
    if D>=0:
        x1 = (-b - math.sqrt(D))/(2*a)
        x2 = (-b + math.sqrt(D))/(2*a)
        return [x1, x2]
    else:
        return None

# Викликати функцію для розв'язку рівняння 2*x^2+3*x-5=0
Res = SquareEquation(2, 3, -5)

if Res!=None:
    print('Result = ', Res)
else:
    print('The equation has no roots')

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

Result =   [-2.5, 1.0]

Приклад 2.

У прикладі реалізована функція CalcComplex(), яка на основі заданого значення n реалізує вкладені функції обчислення:

  • суми комплексних чисел – функція AddComplex();
  • різниці комплексних чисел – функція SubComplex().

Функція CalcComplex() повертає результат у вигляді списку.

# Вкладені функції

import math

# Функція CalcComplex(), яка на основі заданого числа викликає
# вкладену функцію обчислення відповідного виразу.
def CalcComplex(n, a1, b1, a2, b2):
    if n==1:
        # Визначити функцію AddComplex() - додавання комплексних чисел
        def AddComplex(a1, b1, a2, b2):
            real = a1+a2 # дійсна частина комплексного числа
            imag = b1+b2 # уявна частина комплексного числа
            return [real, imag]

        # Викликати функцію AddComplex()
        result = AddComplex(a1, b1, a2, b2)

        # повернути результат
        return result

    if n==2:
        # Визначити функцію SubComplex()
        def SubComplex(a1, b1, a2, b2):
            real = a1-a2 # дійсна частина
            imag = b1-b2 # уявна частина
            return [real, imag]

        # Викликати функцію SubComplex()
        result = SubComplex(a1, b1, a2, b2)

        # Повернути результат
        return result

    # Інакше повернути None
    return None

# Знайти суму комплексних чисел:
# (2-3*j) + (-2+5*j)
Result = CalcComplex(1, 2, -3, -2, 5)

# Вивести результат
print("Result = ", Result)

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

Result = [0, 2]

 

4. Фабричні функції. Основні поняття. Приклад

Як відомо, при виклику функції створюється об’єкт цієї функції. Цей об’єкт має свою адресу (посилання). У випадку вкладених функцій можна зберегти об’єкт вкладеної функції в охоплюючій функції. Якщо збережено об’єкт вкладеної функції, то така функція називається фабричною функцією. При цьому до охоплюючої функції ставиться вимога: охоплююча функція повинна повернути посилання на об’єкт вкладеної функції в операторі return.

Після оголошення фабричної функції у програмі, її можна використовувати наступним чином:

  • створювати об’єкт фабричної функції з допомогою виклику охоплюючої функції. При цьому створюється посилання на об’єкт фабричної функції. Крім того, в об’єкті фабричної функції запам’ятовуються усі значення, які були їй передані з охоплюючої функції;
  • викликати фабричну функцію з допомогою створеного посилання.

У загальному випадку, процес створення та використання фабричної функції виглядає наступним чином.

1. Оголосити вкладені функції як показано нижче

def Fn1(parameters1):
    ...
    def Fn2(parameters2): # це є фабрична функція
        ...
        # використання параметрів parameters1 охоплюючої функції
        # ...
        ...
        return value # функція Fn2() повертає деякий результат
    ...
    return Fn2 # функція Fn1() повертає посилання на об’єкт функції Fn2()

2. Отримати посилання на об’єкт вкладеної фабричної функції. Щоб отримати посилання, потрібно звернутись до охоплюючої функції Fn1() з аргументами, які їй передаються

ref = Fn1(arguments1)

У цьому випадку аргументи arguments1 охоплюючої функції Fn1() конвертуються в параметри parameters1. Ці параметри використовуються і запам’ятовуються в об’єкті вкладеної функції Fn2().

3. Викликати об’єкт фабричної функції Fn2() за посиланням ref

ref(arguments2)

тут arguments2 конвертуються в parameters2 вкладеної функції Fn2.

Таким чином, у програмі виникає виклик фабричної функції

Fn1(arguments1)(arguments2)

Приклад 1. Функція, яка обчислює факторіал числа n: n! = 1·2·3· … ·n.

# Фабричні функції. Обчислення факторіалу числа

# 1. Визначити зовнішню функцію Factorial(n),
#    яка просто оголошує і повертає вкладену функцію Fact()
def Factorial(n):
    # Оголосити вкладену функцію обчислення факторіалу
    def Fact():
        # функція Fact() запам'ятовує значення n
        # з охоплюючої області видимості і використовує його
        if (n<=0):
            return 0
        i=1
        res=1
        while i<=n:
            res=res*i
            i=i+1
        return res

    # Повернути результат функції Fact не викликаючи її
    return Fact

# 2. Запам'ятати посилання на функцію Factorial(5),
#    яка обчислює 5!
ref5 = Factorial(5)

# Вивести посилання
print("ref5 = ", ref5) # ref5 = <function Factorial.<locals>.Fact at 0x035176A8>

# Викликати функцію обчислення факторіалу 5! за посиланням
# та отримати результат
F5 = ref5()
print("5! = ", F5) # 5! =   120

# 3. Отримати інше посилання на функцію, яка обчислює 8!
ref8 = Factorial(8)

# Вивести посилання ref8 та результат обчислення 8!
print('ref8 = ', ref8)
print("8! = ", ref8())

# 4. Одразу звернутися до функції обчислення факторіалу 10! через посилання
print("10! = ", Factorial(10)())

# 5. Знову викликати функцію обчислення 5! через посилання ref5,
#   посилання ref5 запам'ятовується
print("5! = ", ref5()) # 5! =   120

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

ref5 = <function Factorial.<locals>.Fact at 0x032E2150>
5! = 120
ref8 = <function Factorial.<locals>.Fact at 0x033376A8>
8! = 40320
10! = 3628800
5! = 120

Приклад 2. Використання фабричної функції, яка отримує параметри. У прикладі оголошуються функції, які обчислюють корінь n-го степеня з числа x. Охоплююча функція отримує значення n, яке запам’ятовується у фабричній функції, яка, в свою чергу, отримує параметр x.

# Вкладені функції. Фабричні функції
# Взяття кореня n-го степеня з числа x
# 1. Оголосити охоплюючу функцію Root() з вкладеною функцією Rt()
def Root(n): # корінь n-го степеня
    # Оголосити вкладену функцію, яка отримує число x
    def Rt(x):
        # у вкладеній функції запам'ятовується параметр n охоплюючої функції
        return x**(1.0/n)

    # Повернути посилання на вкладену функцію
    return Rt

# 2. Запам'ятати посилання на охоплюючу функцію з параметром 3
ref = Root(3) # ref = <function Root.<locals>.Rt at 0x0032B588>
print('ref = ', ref)

# 3. Обчислити корінь 3-го степеня з числа 27
result = ref(27) # викликаєтсья Root(3)(27)
print('27^(1/3) = ', result)

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

ref = <function Root.<locals>.Rt at 0x030E2150>
27^(1/3) = 3.0

 

5. Передача даних з охоплюючої області видимості у вкладену функцію з допомогою аргументів за замовчуванням. Приклад

З об’ємлючої функції можна передати дані у вкладену функцію з допомогою аргументів за замовчуванням. Загальна форма такої передачі наступна

def FnOut:
    varOut = value
    def FnIn(varIn = varOut): # varOut - аргумент за замовчуванням
        # використання varIn
        ...
        return
    ...
    return

тут

  • FnOut – охоплююча функція;
  • FnIn – вкладена функція;
  • varOut – змінна в охоплюючій функції, яка передається у внутрішню функцію;
  • value – значення, яке отримує змінна в varOut в охоплюючій функції;
  • varIn – змінна у вкладеній функції, якій передається значення змінної varOut охоплюючої функції. Передача значення відбувається у вигляді рядка varIn=varOut.

Приклад.

У прикладі реалізується обчислення площі трикутника за формулою Герона. У вкладену функцію Semiperimeter передаються три аргументи за замовчуванням з функції AreaTriangle.

# Вкладені функції

# Підключити математичний модуль
import math

# Визначити функцію AreaTriangle(),
# яка обчислює площу трикутника за формулою Герона
# на основі довжин його сторін a, b, c.
def AreaTriangle(a, b, c):
    # Перевірка, чи з сторін a, b, c можна утворити трикутник
    if (((a+b)<c)or((b+c)<a)or((a+c)<b)):
        return 0

    # У функції AreaTriangle() визначається
    # вкладена функція Semiperimeter(), яка отримує параметри a, b, c
    # охоплюючої функції і на основі цих параметрів обчислює півпериметер.
    # Параметри a, b, c отримуються як аргументи за замовчуванням
    def SemiPerimeter(a=a, b=b, c=c):
        return (a+b+c)/2

    # Обчислити півпериметер
    p = SemiPerimeter(a, b, c)

    # Обчислити площу
    area = math.sqrt(p*(p-a)*(p-b)*(p-c))

    return area

# Продемонструвати роботу функції AreaTriangle()
Area = AreaTriangle(5, 7, 3)
print('Area = ', Area)

Area = AreaTriangle(1, 1, 5)
print('Area = ', Area)

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

Area = 6.49519052838329
Area = 0

 


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