Python декораторы

07 Мар 2021 , 119

Декораторы - одни из самых часто используемых инструментов в Python 

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


from django.views.decorators.cache import cache_page

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

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

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


from fastapi import FastAPI

app = FastAPI()


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

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

Допустим у вас есть вьюха в виде функции на Django, которую хотите показывать только для зарегистрированных пользователей. Вы конечно , можете написать код, внутри вьюхи, где проверяется авторизован пользователь или нет или же просто использовать декоратор login_required , который присутствует в Django. Либо же сами написать свой декоратор. Ну а чтобы писать свои декораторы , нужно разобраться , что такое декоратор и как его реализовать. Давайте восполним этот пробел.

Как мы знаем(если не знали, то сделаете вид , что знаете) , в 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, внутри которой мы вызываем функцию


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


def cache_response(item):
    def actual_dec(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(item)
            print(args)
            print(kwargs)
            print("before function")
            func(*args, **kwargs)
            print("after function")

        return wrapper
    return actual_dec


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



comments powered by Disqus

Подписка

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

Рубрики

Теги