Вопрос с собеседования · Python

Что такое замыкание?

JuniorФункции
Ещё спрашивают так:
  • Как работают замыкания в Python?
  • Зачем нужен nonlocal?
  • Почему лямбды в цикле возвращают одно и то же значение?
Коротко

Замыкание — вложенная функция, которая помнит переменные внешней функции после её завершения.

  • Захватывается сама переменная, а не значение на момент создания
  • Чтобы присвоить захваченной переменной, нужен nonlocal

А ещё Ловушка: лямбды в цикле видят последнее значение счётчика

Подробно

Если внутренняя функция использует имя из объемлющей функции, Python сохраняет ссылку на эту переменную в «ячейке» (cell). Внешняя функция может завершиться, а внутренняя продолжит видеть и читать эту переменную. Ячейки видны в func.__closure__.

Зачем: фабрики функций (make_multiplier(3)), декораторы, колбэки с контекстом, простое хранение состояния без класса.

Присваивание. Если внутри вложенной функции присвоить имени значение, Python считает его локальным. Чтобы изменить захваченную переменную, её объявляют nonlocal (для глобальной — global).

Позднее связывание. Замыкание хранит переменную, а не её значение в момент создания. Классическая ловушка — функции, созданные в цикле: все они ссылаются на одну переменную цикла и видят её последнее значение. Решения — аргумент по умолчанию (lambda i=i: i) или functools.partial.

Пример

def counter():
    count = 0
    def inc():
        nonlocal count
        count += 1
        return count
    return inc

c = counter()
c(), c()                      # (1, 2)

funcs = [lambda: i for i in range(3)]
[f() for f in funcs]          # [2, 2, 2] — позднее связывание

funcs = [lambda i=i: i for i in range(3)]
[f() for f in funcs]          # [0, 1, 2]

Что хотят услышать

  • Определение: вложенная функция + захват переменных объемлющей области.
  • Где применяется: декораторы, фабрики функций.
  • nonlocal для изменения захваченной переменной.
  • Позднее связывание и способ его обойти.

Частые ошибки

  • «Замыкание копирует значение переменной» — хранится ссылка на переменную.
  • Получить UnboundLocalError из-за присваивания без nonlocal.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗Вложенная функция, которая копирует значения внешних переменных в момент своего создания
  • ✗Функция, которая автоматически закрывает файл или соединение после работы
  • ✓Вложенная функция, помнящая переменные внешней после её завершения
  • ✗Присвоить захваченной переменной во вложенной функции можно без nonlocal

Что спросят дальше

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования