← Назад к вопросам
Что замыкается в замыкании?
2.8 Senior🔥 181 комментариев
#DevOps и инфраструктура#Django
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Замыкания (closures) в Python
Определение
Замыкание — это функция, которая запоминает и имеет доступ к переменным из внешней (охватывающей) области видимости, даже после того как эта область завершила выполнение. То есть, замыкается доступ к переменным из внешней функции.
Простой пример
def outer_function(x):
"""Внешняя функция"""
def inner_function(): # Это будет замыканием
"""Вложенная функция имеет доступ к x"""
return x + 10
return inner_function
# Создаём замыкание
closure = outer_function(5)
print(closure()) # 15 — замыкание помнит x = 5
# Даже после завершения outer_function, closure помнит x
closure2 = outer_function(20)
print(closure2()) # 30 — это другое замыкание со своим x
Что замыкается? Переменная x из области видимости outer_function. Это и есть замыкание.
Что нужно знать о замыканиях
1. Замыкание захватывает переменные, а не значения
def create_multiplier(n):
def multiply(x):
return x * n # Замыкание захватывает переменную n
return multiply
mult_by_3 = create_multiplier(3)
mult_by_5 = create_multiplier(5)
print(mult_by_3(10)) # 30 — использует свою n=3
print(mult_by_5(10)) # 50 — использует свою n=5
# Каждое замыкание имеет свой набор захватанных переменных
2. Замыкание может модифицировать внешние переменные
def counter():
count = 0 # Переменная из внешней области
def increment():
nonlocal count # Указываем, что используем переменную из внешней области
count += 1
return count
return increment
cnt = counter()
print(cnt()) # 1
print(cnt()) # 2
print(cnt()) # 3
# Замыкание сохраняет состояние count между вызовами
3. nonlocal vs global
global_var = "глобальная"
def outer():
outer_var = "внешняя"
def inner():
local_var = "локальная"
# global_var — глобальная переменная
# outer_var — из замыкания (nonlocal)
# local_var — локальная переменная
print(f"Global: {global_var}")
print(f"Outer: {outer_var}")
print(f"Local: {local_var}")
return inner
f = outer()
f()
# Global: глобальная
# Outer: внешняя
# Local: локальная
Различие:
- nonlocal — переменная из немедленно охватывающей области (родительская функция)
- global — переменная из глобальной области модуля
Практические примеры замыканий
1. Декораторы (частый случай замыканий)
def timer_decorator(func):
"""Декоратор использует замыкание для доступа к func"""
def wrapper(*args, **kwargs):
import time
start = time.time()
result = func(*args, **kwargs) # Замыкание захватывает func
print(f"Время: {time.time() - start:.4f}s")
return result
return wrapper
@timer_decorator
def slow_function():
import time
time.sleep(1)
return "готово"
slow_function()
# Время: 1.0023s
# готово
2. Кэширование (мемоизация)
def memoize(func):
"""Замыкание запоминает результаты вызовов"""
cache = {} # Захватана в замыканию
def wrapper(x):
if x not in cache:
cache[x] = func(x) # Сохраняем результат
return cache[x]
return wrapper
@memoize
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(35)) # Быстро, благодаря кэшу в замыканию
3. Фабрика функций
def power_factory(exp):
"""Создаёт функцию возведения в степень"""
def power(base):
return base ** exp # Замыкание захватывает exp
return power
square = power_factory(2)
cube = power_factory(3)
print(square(5)) # 25 (5^2)
print(cube(5)) # 125 (5^3)
4. Частичное применение (currying)
def add(a):
"""Замыкание для создания функции сложения"""
def inner(b):
return a + b # Замыкание захватывает a
return inner
add_5 = add(5)
print(add_5(3)) # 8
print(add_5(10)) # 15
# Это замыкание, создающее специализированные функции
Визуализация замыкания
def outer(x):
print(f"outer() вызвана с x={x}")
def inner():
print(f"inner() вызвана, x всё ещё {x}")
return x
print("outer() возвращает inner")
return inner
# Вызов outer
closure = outer(42)
print("\n---\n")
# Вызов замыкания
result = closure()
# Выход:
# outer() вызвана с x=42
# outer() возвращает inner
#
# ---
#
# inner() вызвана, x всё ещё 42
# x="42" всё ещё доступен в замыканию, даже после outer завершилась!
Просмотр захватанных переменных
def create_greeting(name):
age = 25 # Эта переменная будет захвачена
def greet():
return f"Привет, {name}, тебе {age} лет"
return greet
closure = create_greeting("Alice")
# Просмотр захватанных переменных
print(closure.__closure__) # Кортеж cell объектов
print(closure.__code__.co_freevars) # Имена свободных переменных
# Выход:
# (<cell at ...: str object at ...>, <cell at ...: int object at ...>)
# ('name', 'age')
# Доступ к значениям:
for i, var_name in enumerate(closure.__code__.co_freevars):
cell = closure.__closure__[i]
print(f"{var_name} = {cell.cell_contents}")
# Выход:
# name = Alice
# age = 25
Изменение переменных в замыканию
def create_counter():
count = 0 # Локальная переменная
def increment():
nonlocal count # ВАЖНО: без nonlocal Python не позволит присваивать
count += 1
return count
def decrement():
nonlocal count
count -= 1
return count
def get_count():
return count
# Возвращаем объект с методами
return {
'increment': increment,
'decrement': decrement,
'get': get_count
}
counter = create_counter()
print(counter['increment']()) # 1
print(counter['increment']()) # 2
print(counter['get']()) # 2
print(counter['decrement']()) # 1
Частая ошибка с замыканиями в циклах
# НЕПРАВИЛЬНО: все замыкания делят одну переменную
def create_functions():
functions = []
for i in range(5):
def func():
return i # Замыкание захватывает i
functions.append(func)
return functions
funcs = create_functions()
for func in funcs:
print(func()) # Выведет: 4, 4, 4, 4, 4 (все используют последнее значение i)
# ПРАВИЛЬНО: Создай отдельное замыкание для каждого значения
def create_functions_correct():
functions = []
for i in range(5):
def func(x=i): # Захватываем значение через параметр по умолчанию
return x
functions.append(func)
return functions
funcs = create_functions_correct()
for func in funcs:
print(func()) # Выведет: 0, 1, 2, 3, 4 (правильно)
# ИЛИ используй lambda с параметром
def create_functions_lambda():
functions = []
for i in range(5):
functions.append(lambda x=i: x)
return functions
funcs = create_functions_lambda()
for func in funcs:
print(func()) # Выведет: 0, 1, 2, 3, 4 (правильно)
Заключение
Что замыкается в замыканию:
- Переменные из внешней области видимости — функция запоминает их
- Состояние — замыкание может сохранять данные между вызовами
- Контекст выполнения — доступ к переменным сохраняется даже после завершения внешней функции
Запомни:
- Замыкание = вложенная функция + захватанные переменные
- Используй
nonlocalдля изменения переменных из внешней области - Будь осторожен с циклами (захватываются переменные, а не значения)
- Замыкания часто используются в декораторах, кэшировании, фабриках функций