💻 Практические примеры

⚡ Минимальный рабочий пример

def multiplier(factor: float):
    def inner(number: float) -> float:
        return number * factor
    return inner


double = multiplier(2)
print(double(5))  # 10.0

Пример 1. Вложенная вспомогательная функция

Вспомогательная функция clean_text используется только внутри process_data и не загрязняет глобальное пространство имён.

def process_data(data: list[str]) -> list[str]:
    def clean_text(text: str) -> str:
        return text.strip().lower()

    return [clean_text(item) for item in data]


items = ["  Apple  ", " BaNaNa ", " CHERRY "]
print(process_data(items))
# ['apple', 'banana', 'cherry']

Пример 2. Замыкание, которое только читает внешнюю переменную

Переменная border читается, но не изменяется, поэтому nonlocal не нужен.

def create_filter(border: float):
    def filter_value(value: float) -> bool:
        return value > border

    return filter_value


greater_than_five = create_filter(5)
print(greater_than_five(7))  # True
print(greater_than_five(3))  # False

Пример 3. Замыкание с изменением состояния

Для изменения count внутри вложенной функции необходим nonlocal.

def make_counter():
    count = 0

    def increment() -> int:
        nonlocal count
        count += 1
        return count

    return increment


counter = make_counter()
print(counter())  # 1
print(counter())  # 2
print(counter())  # 3

Пример 4. Фабрика функций умножения

Каждый вызов multiplier создаёт отдельное замыкание со своим factor.

def multiplier(factor: float):
    def multiply(number: float) -> float:
        return number * factor

    return multiply


double = multiplier(2)
triple = multiplier(3)

print(double(4))  # 8
print(triple(4))  # 12

Пример 5. Альтернатива замыканию — functools.partial

Для простых фабрик удобнее использовать встроенный инструмент, а не писать своё замыкание.

from functools import partial
from operator import mul


double = partial(mul, 2)
triple = partial(mul, 3)

print(double(4))  # 8
print(triple(4))  # 12
Проверить по документации: functools.partial подходит, когда нужно зафиксировать часть аргументов уже существующей функции. Подробнее — в документации functools.

Пример 6. Исправление позднего связывания

Без захвата переменной цикла все лямбды получат одно и то же значение.

# Неправильно
functions_wrong = [lambda x: x * i for i in range(3)]
print([f(2) for f in functions_wrong])  # [4, 4, 4]

# Правильно: захватываем текущее i через значение по умолчанию
functions_right = [lambda x, i=i: x * i for i in range(3)]
print([f(2) for f in functions_right])  # [0, 2, 4]

Пример 7. Как посмотреть замыкание

Атрибут __closure__ позволяет увидеть захваченные переменные.

def make_multiplier(factor: float):
    def multiply(number: float) -> float:
        return number * factor
    return multiply


double = make_multiplier(2)
print(double.__closure__)                  # (<cell at ...>,)
print(double.__closure__[0].cell_contents)  # 2

Как запустить в VS Code

  1. Создайте папку python-fundamentals-practice/lesson-62/.
  2. Создайте файл closures.py и скопируйте в него примеры.
  3. Откройте терминал в VS Code (Ctrl + `).
  4. Активируйте виртуальное окружение: venv\Scripts\activate (Windows) или source venv/bin/activate (Mac/Linux).
  5. Запустите: python closures.py