обзор декораторов функций

Создание и применение декораторов функций в Python

Декораторы в Python — это мощный и гибкий инструмент, который позволяет изменять поведение функций или классов, не изменяя их исходный код. В этой статье мы подробно рассмотрим декораторы функций.

Что такое декораторы

Декораторы — это, по сути, функции, которые «обертывают» другую функцию, модифицируя её поведение.

Это делает их идеальным инструментом для выполнения таких задач, как логирование, кэширование, проверка типов, измерение времени выполнения и многое другое.

def my_decorator(func):
    def wrapper():
        print("Before the function call")
        func()
        print("After the function call")
    return wrapper

@my_decorator
def greet():
    print("Hello, world!")

greet()

#Before the function call
#Hello, world!
#After the function call

В этом примере my_decorator — это декоратор. Когда мы вызываем greet(), на самом деле вызывается wrapper(), который в свою очередь вызывает greet().

Принцип работы декораторов

Декораторы работают, используя два ключевых принципа Python:

  1. Функции являются объектами. В Python функции могут быть присвоены переменным, сохранены в структурах данных, переданы в качестве аргументов другим функциям и даже возвращены другими функциями в качестве результата.
  2. Функции могут быть определены внутри других функций. В Python функции могут быть определены в любом месте, где допустимы операторы, включая тело другой функции. Такие функции называются вложенными функциями.

Создание декораторов

Декоратор обычно определяется как функция, которая принимает один аргумент (функцию для декорирования) и возвращает новую функцию, которая заменяет оригинальную.

def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

Применение декораторов

Чтобы применить декоратор к функции, его имя указывается перед определением функции, предварительно снабдив его символом @.

@my_decorator
def greet():
    print("Hello, world!")

greet()

Передача аргументов в декорируемую функцию

В текущем виде наш декоратор может работать только с функциями, которые не принимают аргументов. Чтобы сделать его более гибким, мы можем использовать *args и **kwargs в определении обертки, чтобы принимать любое количество позиционных и именованных аргументов.

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        func(*args, **kwargs)
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def greet(name):
    print(f"Hello, {name}!")

greet("Alice")

#Something is happening before the function is called.
#Hello, Alice!
#Something is happening after the function is called.

Возвращаемые значения из декорированных функций

В настоящее время наш декоратор не возвращает никакого значения из декорируемой функции. Чтобы исправить это, мы должны вернуть значение функции из нашего оберточного метода.

def my_decorator(func):
    def wrapper(*args, **kwargs):
        print("Something is happening before the function is called.")
        result = func(*args, **kwargs)
        print("Something is happening after the function is called.")
        return result
    return wrapper

Практические примеры декораторов

Декораторы могут использоваться для самых разнообразных задач. Вот несколько примеров.

1. Логирование

import logging

def log_decorator(func):
    def wrapper(*args, **kwargs):
        logging.info(f"Running {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_decorator
def some_function():
    print("Running some_function")

some_function()

2. Измерение времени выполнения

import time

def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        print(f"Function {func.__name__} took {end_time - start_time:.4f} seconds to run.")
        return result
    return wrapper

@timer_decorator
def slow_function():
    time.sleep(2)

slow_function()

3. Кэширование результатов

import functools

def cache_decorator(func):
    cache = dict()
    @functools.wraps(func)
    def wrapper(*args):
        if args in cache:
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    return wrapper

@cache_decorator
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))

Заключение

Декораторы представляют собой мощный механизм для модификации поведения функций и методов в Python. Они обеспечивают простой и единообразный способ изменения поведения функций и методов без необходимости изменять исходный код этих функций и методов. Их возможности включают логирование, кэширование, изменение аргументов или результатов и многое другое.

Содержание: