Простейшее руководство для декораторов Python

Добавить «Pythonic» оболочку любых функций / классов, не изменяя их.

Одна из самых уникальных особенностей языка программирования Python - это декоратор. Это знак «@», который вы, возможно, видели повсюду в коде Python. Конечно, декоратор есть и во многих других языках, но концепции другие. В Python декоратор можно очень легко определить и использовать для любых функций и классов.

Это похоже на «обертку» функций или классов, которые мы использовали для добавления дополнительных вариантов их поведения, не изменяя их. Это очень полезно, когда мы хотим

  1. Добавьте некоторые варианты поведения к встроенным или сторонним функциям / классам, чтобы нам не нужно было разветвлять и изменять исходный код.
  2. Повторно используйте это настроенное поведение для нескольких функций / классов.

Есть много замечательных библиотек Python, использующих декораторы, такие как attr и marshmallow, которые представлены в статьях ниже.

Вероятно, лучшая практика объектно-ориентированного Python - Attr
https://towardsdatascience.com/probably-the-best-practice-of-object-oriated-python-attr-d8c26c0e8a4

Самое элегантное объектно-ориентированное программирование на Python
https://towardsdatascience.com/the-most-elegant-python-object-oriated-programming-b38d75f4ae7b

В этой статье я попытаюсь продемонстрировать, как определить и использовать декоратор Python самым простым способом. Надеюсь, это поможет людям, которые не знают или не совсем знакомы с этим.

Простое определение

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

def function_decorator(func):
    def wrapped_func():
        # Do something before the function is executed
        func()
        # Do something after the function has been executed
    return wrapped_func

Приведенный выше код - это определение декоратора, украшающего функцию.

  • function_decorator - имя декоратора.
  • wrapped_func - это имя внутренней функции, которая фактически используется только в этом определении декоратора.
  • func - декорируемая функция.

Во внутренней функции wrapped_func мы можем делать все, что до и после вызова func. После того, как декоратор определен, мы просто используем его следующим образом.

@function_decorator
def func():
    pass

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

Кстати, способ определения декоратора на самом деле использует закрытие Python, что является еще одной особенностью Python. Если вас это интересует, ознакомьтесь с одной из моих предыдущих статей для получения более подробной информации.



Основное использование

Я знаю, никому не нравятся только концепции. Вот простой пример декоратора Python.

def function_decorator(func):
    def wrapped_func():
        print('=' * 30)
        func()
        print('=' * 30)
    return wrapped_func

Затем давайте воспользуемся этим декоратором.

@function_decorator
def test():
    print('Hello World!')

Вот результат.

В этом декораторе мы выводим 30 знаков равенства «=» до и после выполнения функции. Следовательно, мы можем сказать, что декоратор используется для добавления верхней и нижней строки = для любых функций, которые что-то печатают.

Передавать аргументы

Что, если мы хотим украсить некоторые функции, ожидающие аргументов? Например, функция ниже берет имя и отправляет приветствие.

@function_decorator
def greeting(name):
    print(f'Hey {name}! Good Morning!')

Давайте попробуем тот же декоратор, который определен выше в разделе, посвященном базовому использованию.

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

def function_decorator(func):
    def wrapped_func(*args, **kwargs):
        print('=' * 30)
        func(*args, **kwargs)
        print('=' * 30)
    return wrapped_func

Единственное отличие состоит в том, что мы добавили *args, **kwargs в качестве аргументов внутренней функции и передали их в func. Это гарантирует, что любые аргументы будут просто переданы, и проблем не возникнет.

Если вы не знаете, что такое *args и **kwargs, многие руководства можно легко найти в Интернете.

Больше примеров

Приведу еще один пример. Мы определенно можем сделать больше, чем просто вывести что-то выше и ниже декорированной функции. Мы можем использовать декорированную функцию как угодно.

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

from datetime import datetime
def timed_log(log_msg):
    def time_added(*args, **kwargs):
        return f'[{datetime.now()}] {log_msg(*args, **kwargs)}'
    return time_added

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

Предположим, у нас есть две разные функции журнала: одна принимает целое число в качестве «номера строки» и указывает пользователям, что там произошла ошибка, а другая просто сообщает пользователю, что все выполнено успешно.

@timed_log
def log_error(line_no):
    return f'There is an error happend at line {line_no}'
@timed_log
def log_done():
    return 'Great! All processed done.'

Обратите внимание, что декоратор был повторно использован для обеих функций.

Украшение класса

Декоратор Python может украшать не только функции, но и классы. Я имею в виду сам класс. Если мы хотим изменить поведение некоторых методов класса, то это ничем не отличается от декоратора функции.

Когда мы применяем декоратор к классу, мы фактически украшаем его метод построения.

def class_decorator(_class):
    def wrapped_class_constructor():
        # Do something before the class is instantiated
        instance = _class()
        # Do something after the class has been instantiated
        return instance
    return wrapped_class_constructor

Следовательно, мы фактически добавляем поведения, когда пытаемся получить экземпляр класса. Обратите внимание, что я использую _class для обозначения класса, потому что class - зарезервированное ключевое слово в Python (как и в большинстве других объектно-ориентированных языков).

Синглтон

Все еще слишком концептуально. Теперь позвольте мне показать вам одно из типичных применений декоратора класса, которым является «Синглтон».

Если вы не знакомы с синглтоном, ничего страшного. Это просто общий шаблон проектирования программирования, определяющий класс, который может иметь только один экземпляр. Это очень полезно, когда мы определяем класс для подключения некоторых ресурсов, таких как подключение к базе данных и HTTP-подключение. мы, конечно же, не хотим иметь новые подключения к базе данных во всех случаях, когда мы выполняем запросы к БД. Поэтому мы хотим иметь только одно соединение и повторно использовать его везде.

Основная идея состоит в том, что мы определим словарь и сохраним в нем экземпляр. Каждый раз, когда мы пытаемся создать экземпляр класса, проверяется словарь. Если экземпляра нет, создайте экземпляр класса и верните экземпляр. В противном случае не создавайте экземпляр класса снова, просто верните существующий экземпляр.

def singleton(_class):
    instances = {}
    def get_instance(*args, **kwargs):
        if _class not in instances:
            print('Connecting to DB...')
            instances[_class] = _class(*args, **kwargs)
            print('Connected')
        else:
            print('Already has a connection, will reuse it.')
        return instances[_class]
    return get_instance

Теперь давайте определим класс подключения к БД. Например, нам не нужно его реализовывать, просто используйте ключевое слово pass. Не забудьте добавить декоратор singleton.

@singleton
class DBConnection:
    def connect_to_db():
        pass

** Бесстыдная реклама ** Если вы хотите узнать больше о Python, ознакомьтесь с одной из моих предыдущих статей об этом.



Почему Python требует утверждения« пройден
Когда использовать пройти и есть ли альтернативы? todatascience.com»



Затем давайте дважды создадим экземпляр класса.

Это показывает, что во второй раз мы не создаем новый экземпляр. Далее мы можем проверить, действительно ли две переменные ссылаются на один и тот же экземпляр.

Резюме

В этой статье я представил Python Decorate и его основные способы использования. Мы можем использовать Python Decorator для любых функций и классов. Когда он украшает класс, он фактически применяется к конструктору.

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

Если вы считаете, что мои статьи полезны, рассмотрите возможность присоединения к Medium-членству, чтобы поддержать меня и тысячи других авторов! ‹- Щелкните