Замыкание (closure) представляет функцию, которая запоминает свое лексическое окружение даже в том случае, когда она выполняется вне своей области видимости.
Технически замыкание включает три компонента:
внешняя функция, которая определяет некоторую область видимости и в которой определены некоторые переменные и параметры - лексическое окружение
переменные и параметры (лексическое окружение), которые определены во внешней функции
вложенная функция, которая использует переменные и параметры внешней функции
Для определения замыканий в Python применяются локальные функции:
def outer(): # внешняя функция n = 5 # лексическое окружение - локальная переменная def inner(): # локальная функция nonlocal n n += 1 # операции с лексическим окружением print(n) return inner fn = outer() # fn = inner, так как функция outer возвращает функцию inner # вызываем внутреннюю функцию inner fn() # 6 fn() # 7 fn() # 8
Здесь функция outer
определяет локальную переменную n - это и есть лексическое окружение для внутренней функции:
Внутри функции outer определена внутренняя функция - локальная функция inner, которая обращается к своему лексическому окружению - переменной n - увеличивает ее значение на единицу и выводит на консоль:
def inner(): # локальная функция nonlocal n n += 1 # операции с лексическим окружением print(n)
Эта локальная функция возвращается функцией outer:
return inner
В программе вызываем функцию outer и получаем в переменную fn
локальную функцию inner:
fn = outer()
Переменная fn
и представляет собой замыкание, то есть объединяет две вещи: функцию и
окружение, в котором функция была создана. И несмотря на то, что мы получили локальную функцию и можем ее вызывать вне ее окружающей функции, в которой она определена,
тем не менее она запомнила свое лексическое окружение и может к нему обращаться и изменять, что мы увидим по консольному выводу:
fn() # 6 fn() # 7 fn() # 8
Кроме внешних переменных к лексическому окружению также относятся параметры окружающей функции. Рассмотрим использование параметров:
def multiply(n): def inner(m): return n * m return inner fn = multiply(5) print(fn(5)) # 25 print(fn(6)) # 30 print(fn(7)) # 35
Здесь внешняя функция - multiply возвращает функцию, которая принимает число и возвращает число.
Вызов функции multiply()
возвращает локальную функцию inner:
def inner(m): return n * m
Эта функция запоминает окружение, в котором она была создана, в частности, значение параметра n. Кроме того, сама принимает параметр и возвращает произведение параметров n и m.
В итоге при вызове функции multiply определяется переменная fn, которая получает локальную функцию inner и ее лексическое окружение - значение параметра n:
fn = multiply(5)
В данном случае параметр n равен 5.
При вызове локальной функции, например, в случае:
print(fn(6)) # 30
Число 6 передается для параметра m локальной функции, которая возвращает произведение n и m, то есть 5 * 6 = 30.
Также можно было бы сократить этот код с помощью лямбд:
def multiply(n): return lambda m: n * m fn = multiply(5) print(fn(5)) # 25 print(fn(6)) # 30 print(fn(7)) # 35