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