Python декораторы

07 Мар 2021 , 758

Декораторы - одни из самых часто используемых инструментов в Python и предназначены для добавления дополнительного функционала данной функции без изменения содержимого

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

Декораторы и их использование в экосистеме Python.

Декоратор — это функция, которая позволяет обернуть другую функцию для расширения её функциональности без непосредственного изменения её кода.

Например , в документации Django вы можете увидеть такой код , где для кеширования вьюхи предлагают использовать декоратор cache_page


from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def get_posts(request):
    ...

Тут для кеширования вьюхи get_post предлагается использовать декоратор cache_page , который принимает в качестве единственного аргумента: длительность кэширования, в секундах. Самое крутое , что этот декоратор мы можем использовать и для других вьюх, которые нужно закешировать.

Или возьмем другой пример. В официальной документации FastApI можем увидеть такой код, где используется декоратор для представления.


from fastapi import FastAPI

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello World"}

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

О функциях в Python

Как мы знаем , в Python все является объектом. Соответственно , и функции являются объектами.

Будучи объектами функции можно сохранять в переменные таким образом:


def print_hello_world():
    print("Hello World!")

#Так как в Python функция является объектом,то мы можем присвоить ее переменной
hello_world = print_hello_world()

Функции можно передавать в качестве аргументов в другую функцию.


def print_hello_world():
    print("Hello World!")

#Функцию print_hello_world передаем в качестве аргумента в другую функцию 
def other_func(print_hello_world):
      #вызываем функцию 
      print_hello_world()

Также мы можем внутри одной функции определить другую функции и возвратить ее в качестве значения:.


>>> def wrapper():
...     def inner():
...         print("this is inner function")
...     return inner
... 
>>> #Присваиваем объект функции переменной
>>> my_func = wrapper
>>> my_func

>>> #Вызываем функцию
>>> my_func()
this is inner function

Из вышесказанного следует , что в Python функции являются объектами первого класса

Написание первого декоратора

Зная все это мы можем написать наш первый декоратор. Вначале определимся , что будет делать этот декоратор.Допустим , у нас есть функция welcome , которая выводит на экран сообщение "Добро пожаловать!!! Вызов оборачиваемой функции". Напишем декоратор , который позволит выводит сообщения до вызова этой функции и после вызова.


def my_first_decorator(func):
    def wrapper():
        print("---Эта строка выведется до вызова оборачиваемой функции---")
        func()
        print("---Эта строка выведется после вызова оборачиваемой функции---")
    return wrapper


@my_first_decorator
def welcome():
    print("Добро пожаловать!!! Вызов оборачиваемой функции")

welcome()

Мы написали наш декоратор my_first_decorator, который в качестве аргумента принимает функцию func, который внутри определяют другую функцию wrapper, внутри которой мы вызываем функцию

Написание первого полезного декоратора

Выше мы написали первый декоратор. Тут мы напишем реальный полезный декоратор , который выполняет полезную работу. Напишем декоратор, который выводит сколько времени работает функция.


import time


#Декоратор для вывода результата выполнения функции
def timeit(func):
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        print(f"Result {func.__name__ } {time.perf_counter() - start}")
        return result
    return wrapper


#Определяем медленную функцию и оборачиваем в ее наш декоратор
@timeit
def slow_func():
     print("Функция начала свою работу")
     time.sleep(2)
     print("Функция завершила свою работу")


Запускаем и видим как выводится результат работы функции

Используем functools.wraps

Реализация декоратора через класс


from functools import wraps

class CacheResponse:
    
    def __init__(self, item=5):
        self.item = item

    def __call__(self, fn):
        @wraps(fn)
        def wrapper(*args, **kwargs):
            print("before function")
            print(self.item)
            fn(*args, **kwargs)
        return wrapper

@CacheResponse(item=10)
def welcome():
    print("hello")


Заключение

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

Также в этой статье показали почему нужно добавлять functools.wraps декораторам и какие проблемы решаются

comments powered by Disqus

Подписка

Подпишитесь на наш список рассылки, чтобы получать обновления из блога

Рубрики

Теги