Замыкания с изменяемыми переменными
Использование nonlocal
Для изменения переменных из внешней области видимости используется ключевое слово nonlocal:
def create_bank_account(initial_balance=0):
"""Создает функцию для управления банковским счетом"""
balance = initial_balance
def account_manager(action, amount=0):
nonlocal balance
if action == "deposit":
balance += amount
return f"Пополнение на {amount}. Баланс: {balance}"
elif action == "withdraw":
if amount <= balance:
balance -= amount
return f"Снятие {amount}. Баланс: {balance}"
else:
return f"Недостаточно средств. Баланс: {balance}"
elif action == "balance":
return f"Текущий баланс: {balance}"
else:
return "Неизвестная операция"
return account_manager
# Создаем счета
alice_account = create_bank_account(1000)
bob_account = create_bank_account(500)
# Операции со счетами
print(alice_account("balance"))
print(alice_account("deposit", 250))
print(alice_account("withdraw", 100))
print(alice_account("withdraw", 2000))
print("\n" + "="*30 + "\n")
print(bob_account("balance"))
print(bob_account("deposit", 100))
print(bob_account("withdraw", 300))
Замыкания с изменяемыми объектами
def create_shopping_cart():
"""Создает корзину покупок с использованием замыкания"""
items = []
def cart_manager(action, item=None, quantity=1):
if action == "add":
items.append({"name": item, "quantity": quantity})
return f"Добавлен товар: {item} (количество: {quantity})"
elif action == "remove":
items[:] = [i for i in items if i["name"] != item]
return f"Товар {item} удален из корзины"
elif action == "list":
if not items:
return "Корзина пуста"
return "Товары в корзине:\n" + "\n".join([f"- {i['name']}: {i['quantity']} шт." for i in items])
elif action == "total":
total_items = sum(item["quantity"] for item in items)
return f"Общее количество товаров: {total_items}"
elif action == "clear":
items.clear()
return "Корзина очищена"
return cart_manager
# Использование корзины покупок
my_cart = create_shopping_cart()
print(my_cart("add", "яблоки", 5))
print(my_cart("add", "молоко", 2))
print(my_cart("add", "хлеб", 1))
print()
print(my_cart("list"))
print()
print(my_cart("total"))
print()
print(my_cart("remove", "молоко"))
print(my_cart("list"))
print()
print(my_cart("clear"))
print(my_cart("list"))
Замыкания в циклах
При работе с замыканиями в циклах важно понимать особенности их поведения:
Проблема с поздним связыванием
# Неправильный подход
def create_functions_wrong():
functions = []
for i in range(3):
def func():
return i # i будет ссылаться на последнее значение из цикла
functions.append(func)
return functions
# Все функции вернут 2 (последнее значение i)
wrong_functions = create_functions_wrong()
print("Неправильный подход:")
for func in wrong_functions:
print(func()) # Вывод: 2, 2, 2
# Правильный подход с использованием параметра по умолчанию
def create_functions_correct():
functions = []
for i in range(3):
def func(x=i): # Захватываем текущее значение i
return x
functions.append(func)
return functions
correct_functions = create_functions_correct()
print("\nПравильный подход:")
for func in correct_functions:
print(func()) # Вывод: 0, 1, 2
# Альтернативный правильный подход с дополнительной функцией
def create_functions_alternative():
functions = []
def make_func(value):
def func():
return value
return func
for i in range(3):
functions.append(make_func(i))
return functions
alternative_functions = create_functions_alternative()
print("\nАльтернативный правильный подход:")
for func in alternative_functions:
print(func()) # Вывод: 0, 1, 2
Производительность и память
Управление памятью в замыканиях
import sys
def analyze_closure_memory():
"""Анализ использования памяти замыканиями"""
def outer_function(data):
large_list = list(range(1000)) # Большой список
small_var = data
def inner_function():
# Используем только small_var, но large_list тоже остается в памяти
return small_var * 2
return inner_function
# Создаем замыкание
closure = outer_function(42)
# Проверяем, какие переменные захвачены
print("Захваченные переменные:", closure.__code__.co_freevars)
# Размер объекта замыкания
print(f"Размер замыкания: {sys.getsizeof(closure)} байт")
return closure
# Пример оптимизированного замыкания
def create_optimized_closure(data):
"""Оптимизированное замыкание, которое не захватывает лишние переменные"""
def inner_function(captured_data=data):
return captured_data * 2
return inner_function
print("Анализ памяти:")
closure1 = analyze_closure_memory()
closure2 = create_optimized_closure(42)
print(f"\nРазмер обычного замыкания: {sys.getsizeof(closure1)} байт")
print(f"Размер оптимизированного замыкания: {sys.getsizeof(closure2)} байт")
Замыкания представляют собой мощный механизм в Python, который позволяет создавать гибкие и элегантные решения для различных задач программирования. Понимание принципов работы замыканий открывает новые возможности для написания функционального кода и помогает лучше понимать многие аспекты языка Python.
Основные преимущества использования замыканий включают возможность создания специализированных функций, инкапсуляцию состояния без использования классов, реализацию декораторов и создание фабрик функций. Замыкания особенно полезны при работе с функциями высшего порядка, мемоизацией, частичным применением функций и созданием конфигурируемых компонентов.
При работе с замыканиями важно учитывать особенности их поведения в циклах, влияние на использование памяти и производительность приложения. Правильное применение замыканий позволяет писать более чистый, читаемый и поддерживаемый код.
Замыкания являются фундаментальным концептом не только в Python, но и во многих других языках программирования. Освоение этой темы поможет вам стать более грамотным программистом и откроет путь к изучению более продвинутых техник функционального программирования.
0 Комментарий(я)
Зарегистрируйтесь чтобы оставить комментарий