В этой лекции мы рассмотрим одну из самых интересных тем в Python – замыкания и декораторы. Эти концепции часто кажутся сложными, но мы разберём всё шаг за шагом с большим количеством примеров, чтобы стало ясно, как и где их применять.
Прежде чем говорить о замыканиях и декораторах, важно понять, как работает область видимости переменных в Python.
Область видимости — это часть программы, где переменная доступна. В Python есть четыре уровня области видимости, которые можно запомнить по аббревиатуре LEGB:
len()
, range()
, print()
и т.д.Пример использования областей видимости:
x = "глобальная переменная"
def outer_function():
y = "переменная внешней функции"
def inner_function():
z = "локальная переменная"
print(x) # доступ к глобальной переменной
print(y) # доступ к переменной внешней функции
print(z) # доступ к локальной переменной
inner_function()
outer_function()
глобальная переменная
переменная внешней функции
локальная переменная
В этом примере:
x
имеет глобальную область видимости, она доступна везде.y
существует в enclosing области — это область внешней функции, доступная внутренней.z
— это локальная переменная функции inner_function
.Замыкание (closure) — это функция, которая "запоминает" свое окружение (переменные, доступные ей при создании) даже после того, как эта функция завершила свою работу и вышла из области видимости. То есть, когда вы вызываете такую функцию позже, она может использовать переменные, которые были в области видимости при её определении.
Замыкания появляются, когда функция объявляется внутри другой функции, и внутренняя функция ссылается на переменные из внешней. Это создаёт устойчивую связь между внутренней функцией и данными, к которым она имела доступ при своём создании.
Пример замыкания:
def outer_function(message):
def inner_function():
print(f'Сообщение: {message}')
return inner_function
# Создаём замыкание
closure = outer_function('Привет, мир!')
# Вызов замыкания
closure() # Вывод: Сообщение: Привет, мир!
outer_function
принимает параметр message
и возвращает внутреннюю функцию inner_function
.inner_function
имеет доступ к переменной message
, даже после того как outer_function
завершила выполнение.message
запоминается и используется в замыкании, когда мы вызываем closure()
.Замыкания позволяют хранить состояние между вызовами функций. Это полезно, когда вам нужно сохранить некоторую информацию для последующего использования, но не хочется хранить её в глобальной области видимости.
Рассмотрим пример счётчика:
def make_counter():
count = 0
def increment():
nonlocal count # доступ к переменной count из внешней функции
count += 1
return count
return increment
counter = make_counter()
print(counter()) # Вывод: 1
print(counter()) # Вывод: 2
print(counter()) # Вывод: 3
make_counter
, которая возвращает внутреннюю функцию increment
.count
запоминается, и каждый вызов counter()
увеличивает её значение. Она сохраняет своё состояние благодаря замыканию.Теперь, когда мы понимаем, как работают замыкания, перейдём к декораторам.
Декоратор — это функция, которая принимает другую функцию в качестве аргумента и модифицирует или дополняет её поведение, возвращая новую функцию.
Декораторы используют замыкания, потому что они позволяют "оборачивать" функции дополнительной логикой, сохраняя при этом оригинальную функцию и её данные.
def decorator(func):
def wrapper():
print('Это код, который выполняется ДО вызова функции')
func()
print('Это код, который выполняется ПОСЛЕ вызова функции')
return wrapper
# Пример использования декоратора
@decorator
def say_hello():
print('Привет, мир!')
say_hello()
decorator
, который принимает функцию func
и возвращает внутреннюю функцию wrapper
.wrapper
добавляется код до и после вызова func()
.@decorator
, и когда мы вызываем say_hello()
, мы видим результат:Это код, который выполняется ДО вызова функции
Привет, мир!
Это код, который выполняется ПОСЛЕ вызова функции
Декораторы могут использоваться для ведения логов. Например, вы можете отслеживать время выполнения функции:
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f'Функция {func.__name__} выполнена за {end - start:.5f} секунд')
return result
return wrapper
@timer
def calculate_sum(n):
return sum(range(n))
calculate_sum(1000000)
timer
оборачивает любую функцию, измеряя время её выполнения.calculate_sum(1000000)
и видим, сколько времени понадобилось для выполнения функции.Другой пример использования — проверка прав пользователя при доступе к функции:
def requires_permission(user_role):
def decorator(func):
def wrapper(*args, **kwargs):
if user_role != 'admin':
print('Доступ запрещён')
return
return func(*args, **kwargs)
return wrapper
return decorator
@requires_permission('user')
def delete_database():
print('База данных удалена')
delete_database() # Вывод: Доступ запрещён
requires_permission
проверяет, обладает ли пользователь правами для выполнения функции.
Вот таблица с основными понятиями из лекции о области видимости, замыканиях и декораторах:
Понятие | Описание | Пример кода | Результат |
---|---|---|---|
Область видимости (Scope) | Часть программы, где переменная доступна. В Python есть 4 уровня области видимости: LEGB. | ||
L (Local scope) | Локальная область. Переменные, определённые внутри функции, доступны только внутри неё. | def inner(): z = "локальная переменная" | z доступна только в функции inner . |
E (Enclosing scope) | Область окружающей функции. Переменные внешней функции доступны внутренней функции. | def outer(): y = "внешняя"; def inner(): print(y) | Внутренняя функция имеет доступ к переменной y . |
G (Global scope) | Глобальная область. Переменные, определённые на уровне модуля, доступны везде в модуле. | x = "глобальная" | x доступна в любой функции этого модуля. |
B (Built-in scope) | Встроенная область. Встроенные функции и константы Python, например, len() , print() . | print(len("строка")) | Вызов встроенной функции len() из любой части программы. |
Замыкание (Closure) | Функция, которая "запоминает" переменные внешней функции даже после её завершения. | def outer(msg): def inner(): print(msg); return inner | Функция inner помнит переменную msg даже после завершения outer . |
Декоратор | Функция, которая изменяет поведение другой функции без изменения её кода. | @decorator def say_hello(): print("Привет!") | Модифицированная функция добавляет действия до и после вызова. |
Логгирование через декоратор | Измерение времени выполнения функции с помощью декоратора. | @timer def calc_sum(n): return sum(range(n)) | Логирует время выполнения функции calc_sum . |
Авторизация через декоратор | Декоратор проверяет права пользователя перед выполнением функции. | @requires_permission('user') def delete_db(): | Ограничивает доступ для пользователей без прав. |
Таблица охватывает ключевые моменты теории, примеры кода и соответствующие результаты выполнения.
Замыкания и декораторы — это мощные инструменты в Python, которые помогают писать более гибкий и читаемый код. Замыкания позволяют сохранять состояние, а декораторы добавляют дополнительные функции к существующим без изменения их структуры.