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

 


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