Что такое замыкание (closure) в Python и как оно работает

Ответ

Замыкание (closure) — это функция, которая "помнит" и имеет доступ к переменным из своей лексической области видимости, даже когда исполнение этой области завершено.

Как это работает:

Когда вложенная функция ссылается на переменную из внешней функции (не являющуюся её локальной переменной), Python связывает эту переменную с вложенной функцией. Эта связь сохраняется в специальном атрибуте __closure__.

Три условия для создания замыкания:

  1. Должна существовать вложенная функция.
  2. Вложенная функция должна ссылаться на переменные из внешней функции (так называемые "свободные переменные").
  3. Внешняя функция должна возвращать вложенную функцию.

Пример:

def outer_func(text):
    # 'text' - это свободная переменная для inner_func
    def inner_func():
        # inner_func "замыкает" в себе переменную text
        print(text)

    return inner_func

# Вызываем внешнюю функцию, она завершает работу,
# но возвращенная inner_func все еще помнит 'text'
printer_hello = outer_func("Hello")
printer_world = outer_func("World")

# Вызываем экземпляры вложенной функции
printer_hello()  # Вывод: Hello
printer_world()  # Вывод: World

# Можно проверить содержимое __closure__
# Это кортеж из объектов-ячеек (cell), хранящих значения
print(printer_hello.__closure__[0].cell_contents) # Вывод: Hello

Практическое применение:

  • Декораторы: Являются самым частым и ярким примером использования замыканий.
  • Фабрики функций: Создание функций с предопределенной конфигурацией (как в примере выше).
  • Сохранение состояния: Реализация объектов с состоянием без использования классов, например, для создания счетчиков или кэширования.

Ответ 18+ 🔞

А, слушай, смотри, вот эта штука — замыкание. Ну, типа, функция, которая, блядь, помнит всё, что было вокруг неё, даже когда всё уже давно кончилось и разъехалось. Как будто она в кармане унесла кусочек того места, где её родили, сука.

Как оно, блядь, работает:

Вот есть у тебя функция внутри функции. И эта внутренняя, мелкая, цепляется взглядом за какую-то переменную снаружи, не свою. И Python такой: «О, сука, привязка!» — и намертво пришивает эту переменную к внутренней функции. Прям в специальный карман, который __closure__ называется. И таскает она его с собой, пока не сдохнет.

Три пункта, чтобы эта магия случилась, ёпта:

  1. Функция внутри функции — это само собой, блядь. Без этого нихуя не выйдет.
  2. Внутренняя функция должна тыкать пальцем в переменную из внешней, типа «это моё!». Это называется «свободная переменная», хотя она уже не очень-то свободная, блядь.
  3. Внешняя функция должна выплюнуть эту внутреннюю наружу, как результат. Не просто вызвать, а именно отдать.

Смотри, как это выглядит вживую, блядь:

def outer_func(text):
    # 'text' — вот она, свободная, как ветер. Пока что.
    def inner_func():
        # А вот inner_func уже на неё глаз положила. Замыкает, сука.
        print(text)

    return inner_func

# Вызываем внешнюю. Она отработала и сдохла, её 'text' должен был испариться.
# Но нет, блядь! Внутренняя функция его утащила с собой.
printer_hello = outer_func("Привет")
printer_world = outer_func("Мир")

# А теперь вызываем этих утырков. И они помнят!
printer_hello()  # Вывод: Привет
printer_world()  # Вывод: Мир

# Можно даже залезть в тот самый карман и посмотреть, что там спизжено.
print(printer_hello.__closure__[0].cell_contents) # Вывод: Привет

А где это, блядь, пригождается в жизни?

  • Декораторы: Это вообще царство замыканий, ёпта. Без них нихуя бы не работало.
  • Фабрики функций: Как в примере выше — штампуешь функции с разным предустановленным текстом, и каждая таскает свой.
  • Хранение состояния без классов: Ну, типа, счётчик сделать, который помнит, сколько раз его дернули. Почти как объект, только проще, блядь. Для хитрожопых.