Серия Python — Часть 19
Декораторы Python — это мощная функция языка, позволяющая изменять поведение функций или классов. Они позволяют добавлять функциональные возможности в существующий код без изменения его структуры. В этой статье мы подробно рассмотрим декораторы и узнаем, как их эффективно использовать и создавать.
Что такое декораторы?
В Python декораторы — это специальные функции, которые оборачивают другие функции или классы, позволяя изменять их поведение. Они следуют определенному синтаксису, используя символ @
, за которым следует имя декоратора, которое помещается над определением функции или класса. Когда вызывается декоративная функция или класс, функция декоратора вызывается автоматически.
Декораторы широко используются в Python для таких задач, как ведение журнала, синхронизация, аутентификация, запоминание и многое другое. Они обеспечивают чистый и элегантный способ расширения функциональности существующего кода, делая его более модульным и пригодным для повторного использования.
Использование декораторов
Чтобы использовать декоратор, вам нужно определить функцию декоратора и применить ее к целевой функции или классу. Давайте начнем с простого примера, который добавляет функции ведения журнала в функцию:
def log_decorator(func): def wrapper(*args, **kwargs): print(f"Calling function: {func.__name__}") return func(*args, **kwargs) return wrapper @log_decorator def greet(name): print(f"Hello, {name}!") greet("Alice")
В приведенном выше примере мы определили функцию-декоратор log_decorator
, которая принимает функцию func
в качестве аргумента. Внутри декоратора мы определяем вложенную функцию wrapper
, которая отвечает за добавление функции ведения журнала. Затем декоратор возвращает функцию wrapper
.
Функция wrapper
принимает любое количество позиционных и ключевых аргументов, используя *args
и **kwargs
соответственно. Он регистрирует сообщение перед вызовом исходной функции func
и возвращает результат.
Чтобы применить log_decorator
к функции greet
, мы используем синтаксис @
и размещаем его над определением функции. Когда вызывается greet
, декоратор вызывается автоматически, и перед выполнением функции печатается сообщение журнала.
Вывод приведенного выше кода будет:
Calling function: greet Hello, Alice!
Как видите, декоратор добавил функциональность логирования без изменения исходного кода функции greet
.
Уроки декорирования
Декораторы также можно применять к классам, изменяя их поведение. Давайте посмотрим на пример, который добавляет функциональность синхронизации к методу класса:
import time def timing_decorator(method): def wrapper(self, *args, **kwargs): start_time = time.time() result = method(self, *args, **kwargs) end_time = time.time() print(f"Execution time: {end_time - start_time} seconds") return result return wrapper class Calculator: def __init__(self): self.total = 0 @timing_decorator def add(self, x, y): self.total = x + y return self.total calculator = Calculator() result = calculator.add(5, 7) print(f"Result: {result}")
В этом примере мы определили функцию-декоратор timing_decorator
, которая принимает метод method
в качестве аргумента. Декоратор создает функцию wrapper
, которая измеряет время выполнения метода. Время начала и окончания записывается с помощью функции time.time()
, а продолжительность распечатывается.
Функция wrapper
принимает self
в качестве первого аргумента, так как это метод класса. Он также принимает любое количество позиционных и ключевых аргументов. Внутри функции wrapper
мы вызываем исходный метод method
, передавая self
, *args
и **kwargs
. После выполнения метода декоратор печатает время выполнения.
Чтобы применить timing_decorator
к методу add
класса Calculator
, мы используем синтаксис @
и размещаем его над определением метода. При вызове add
автоматически вызывается декоратор, измеряющий время выполнения.
Вывод приведенного выше кода будет примерно таким:
Execution time: 0.000123 seconds Result: 12
Как видите, декоратор добавил в метод add
функциональные возможности измерения времени, измеряя время выполнения без изменения исходного кода.
Создание декораторов
Теперь, когда мы увидели, как использовать декораторы, давайте рассмотрим, как создавать собственные декораторы. Декораторы — это просто функции, которые принимают другую функцию (или класс) в качестве аргумента и возвращают ее измененную версию.
Вот пример создания декоратора, который преобразует результат функции в верхний регистр:
def uppercase_decorator(func): def wrapper(*args, **kwargs): result = func(*args, **kwargs) return result.upper() return wrapper @uppercase_decorator def greet(name): return f"Hello, {name}!" result = greet("Alice") print(result)
В этом примере мы определили функцию-декоратор uppercase_decorator
, которая принимает функцию func
в качестве аргумента. Внутри декоратора мы определяем функцию wrapper
, которая вызывает исходную функцию и преобразует ее результат в верхний регистр с помощью метода upper()
.
Измененный результат затем возвращается декоратором. При вызове greet
автоматически вызывается декоратор, преобразующий приветственное сообщение в верхний регистр.
Вывод приведенного выше кода будет:
HELLO, ALICE!
Как видите, декоратор изменил результат функции greet
, преобразовав его в верхний регистр.
Обработка аргументов в декораторах
Декораторы могут обрабатывать различные типы аргументов, передаваемых декорируемой функции или классу. Есть три общих шаблона для обработки аргументов в декораторах:
- Декораторы без аргументов
- Декораторы с аргументами
- Декораторы, которые можно использовать с аргументами или без них
Давайте рассмотрим каждый шаблон на примерах.
Декораторы без аргументов
Декораторы без аргументов — самая простая форма. Вот пример декоратора, который печатает сообщение до и после декорируемой функции:
def message_decorator(func): def wrapper(*args, **kwargs): print("Before function execution") result = func(*args, **kwargs) print("After function execution") return result return wrapper @message_decorator def greet(name): print(f"Hello, {name}!") greet("Alice")
В этом примере message_decorator
не принимает никаких дополнительных аргументов. Функция wrapper
просто выводит сообщение до и после вызова исходной функции.
Вывод приведенного выше кода будет:
Before function execution Hello, Alice! After function execution
Декораторы с аргументами
Декораторы также могут принимать аргументы, что позволяет настраивать их поведение. Вот пример декоратора, который повторяет выполнение функции заданное количество раз:
def repeat_decorator(repeats): def decorator(func): def wrapper(*args, **kwargs): for _ in range(repeats): result = func(*args, **kwargs) return result return wrapper return decorator @repeat_decorator(3) def greet(name): print(f"Hello, {name}!") greet("Alice")
В этом примере мы определили функцию-декоратор repeat_decorator
, которая принимает аргумент repeats
. Этот аргумент представляет количество раз, которое декорированная функция будет выполняться. Функция repeat_decorator
возвращает другую функцию-декоратор decorator
, которая принимает целевую функцию func
в качестве аргумента.
Внутри функции wrapper
мы используем цикл для повторения выполнения исходной функции repeats
раз. После цикла возвращается результат последнего выполнения.
Чтобы применить repeat_decorator
с определенным количеством повторений к функции greet
, мы используем синтаксис @
и указываем аргумент в круглых скобках. В данном случае @repeat_decorator(3)
означает, что функция greet
будет выполнена три раза.
Вывод приведенного выше кода будет:
Hello, Alice! Hello, Alice! Hello, Alice!
Как видите, декоратор трижды повторил выполнение функции greet
.
Декораторы, которые можно использовать с аргументами или без них
Также можно создавать декораторы, которые можно использовать с дополнительными аргументами или без них. Вот пример декоратора, который добавляет префикс к выходным данным функции с необязательным аргументом префикса:
def prefix_decorator(prefix=None): def decorator(func): def wrapper(*args, **kwargs): result = func(*args, **kwargs) if prefix: return f"{prefix} {result}" return result return wrapper return decorator @prefix_decorator def greet(name): return f"Hello, {name}!" @prefix_decorator("Goodbye") def farewell(name): return f"Goodbye, {name}!" result1 = greet("Alice") result2 = farewell("Bob") print(result1) print(result2)
В этом примере функция prefix_decorator
предназначена для обработки обоих случаев: с аргументом или без него. Если аргумент не указан, префикс будет None
. Внутри функции wrapper
мы проверяем, имеет ли prefix
значение, и если да, то добавляем его к результату.
При применении prefix_decorator
без аргумента, как в @prefix_decorator
, префикс будет None
. При применении его с аргументом, как в @prefix_decorator("Goodbye")
, будет использоваться указанный префикс.
Вывод приведенного выше кода будет:
Hello, Alice! Goodbye Bob!
Как видите, декоратор добавил указанный префикс к выводу функции farewell
, оставив вывод функции greet
без изменений.
Заключение
Декораторы Python — это мощная функция, позволяющая изменять поведение функций или классов без изменения их исходного кода. Они обеспечивают чистый и элегантный способ добавления функциональности к существующему коду, делая его более модульным и пригодным для повторного использования. Понимая, как использовать и создавать декораторы, вы можете расширить возможности своих программ на Python и улучшить читаемость кода.
В этой статье мы изучили основы декораторов, как использовать их для изменения функций и классов и как создавать свои собственные декораторы. Мы также рассмотрели различные шаблоны для обработки аргументов в декораторах. Обладая этими знаниями, вы можете начать использовать возможности декораторов в своих собственных проектах Python.
Декораторы — обширная тема с многочисленными возможностями и вариантами использования. Продолжая свое знакомство с Python, не стесняйтесь изучать более продвинутые концепции и методы, связанные с декораторами.
LinkedIn, Medium, Instagram, Kaggle и GitHub.
Если вам нравится читать истории, подобные этой, и вы хотите поддержать меня, подумайте о том, чтобы стать участником Medium. Взяв 5 долларов в месяц, вы открываете неограниченный доступ к историям на Medium. Если вы воспользуетесь моей ссылкой для регистрации, я получу небольшую комиссию.
Уже участник? Подпишитесь, чтобы получать уведомления, когда я опубликую.
Дополнительные материалы на PlainEnglish.io.
Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .