Одно из интересных понятий функционального программирования — это замыкания (closure). Эта идея оказалась настолько заманчивой для многих разработчиков, что была реализована даже в некоторых нефункциональных языках программирования (Perl). Девид Мертц приводит следующее определение замыкания: "Замыкание - это процедура вместе с привязанной к ней совокупностью данных" (в противовес объектам в объектном программировании, как: "данные вместе с привязанным к ним совокупностью процедур" ).
В питоне не слишком часто используют замыкания явно, в то время, как, скажем, в javascript без замыканий обойтись практически невозможно.
Смысл замыкания состоит в том, что определение функции "замораживает" окружающий её контекст на момент определения. Это может делаться различными способами, например, за счёт параметризации создания функции. При замыкании будут использованы переменные из объемлющих функций. Для того, чтобы переменная попала в замыкание, она должна быть упомянута в коде функции:
def print_msg(msg):
    def fun():
        print(msg)
    return fun  # Помните, что в питоне всё - это объект. Функция тоже, её можно вернуть и воспользоваться потом.
 
printer = print_msg("Hello")
printer()
>>>Hello
def multiplier( n ):  # multiplier возвращает функцию умножения на n
    def mul( k ):
        return n * k
    return mul
 
mul3 = multiplier(3# mul3 - функция, умножающая на 3
print(mul3(3), mul3(5))
>>> 9 15

Правильно захваченные переменные при этом можно даже успешно модифицировать:

def tester(start):
    state = start  # В каждом вызове сохраняется свое значение state
    def nested(label):
        nonlocal state      # Объект state находится
        print(label, state) # в объемлющей области видимости
        state += 1 # Изменит значение переменной, объявленной как nonlocal
    return nested
 
>>> F = tester(0)
>>> F('spam')          # Будет увеличивать значение state при каждом вызове
spam 0
>>> F('ham')
ham 1
>>> F('eggs')
eggs 2

Но вообще тема эта достаточно сложная. В результате неаккуратного использования может быть отстрелено произвольное количество конечностей. Например, нужно иметь в виду, что в замыкание попадает имя переменной, а не её значение.

def gen_adders():
    i = 5
    def add5(x):
        return x + i
    i = 10
    def add10(x):
        return x + i
    i = 15
    return add5, add10
 
adder1, adder2 = gen_adders()
print(adder1(0))  # -> 15
print(adder2(0))  # -> 15
В данном случае обе функции используют одну и ту же захваченную из функции gen_adders переменную i, что и приводит к такому «неожиданному» поведению.
Последнее изменение: Суббота, 15 Август 2020, 02:35