Декораторы

Декораторы

@Pycoders

Декоратор - это функция, которая принимает функцию и возвращает функцию. Звучит странно, но лучше запомните именно такое определение, это достаточно важно для понимания.

А вообще, декораторы по сути являются некими модификаторами к нашим функциям.

Первый декоратор

Итак, для начала создадим простой декоратор, который возвращает принимаемую функцию.

def decorator(func):
    return func

@decorator
def hello():
    print('Hello!')

Если вызвать функцию hello, то вывод будет "Hello!".

Применяют декораторы через символ @. Однако это всё так называемый синтаксический сахар, в рельности то же самое могло выглядеть так.

hello = decorator(hello)

Возврат функции

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

def decorator(func):
    def new_func():
        pass
    return new_func

@decorator
def hello():
    print('Hello!')

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

print(hello.__name__)

Посмотрев __name__ функции hello, то мы действительно увидим 'new_func', а не 'hello'.

Простой логгер

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

def logger(func):
    def wrapped(*args, **kwargs):

        result = func(*args, **kwargs)

        with open('log.txt', 'w') as f:
            f.write(str(result))

        return result    
    return wrapped


@logger
def summator(num_list):
    return sum(num_list)

Сначала была создана функция-декоратор logger, которая принимает в аргументы декорируемую функцию, в данном случае, summator. Внутри самого logger'а новая функция wrapped, которая принимает то, что мы передаем в summator.

В теле функции wrapped вызывается переданная в logger функция, то есть summator, результат которой присваивается в переменную result. А дальше результат просто записывается в файл и возвращается через return.

На всякий случаей вызовем summator и посмотрим содержимое файла.

summator([1, 2, 3])

with open('log.txt', 'r') as f:
    print(f.read())

Программа вывела число 6, значит, всё работает.

Передача аргумента в декоратор

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

def logger(filename):
    def decorator(func):
        def wrapped(*args, **kwargs):

            result = func(*args, **kwargs)

            with open(filename, 'w') as f:
                f.write(str(result))

            return result
        return wrapped
    return decorator


@logger('new_log.txt')
def summator(num_list):
    return sum(num_list)

В logger передаем значение, из логгера возвращается decorator, который возвращает wrapped, который уже записывает result и отдаёт его. В принципе, схема работы здесь аналогичная, просто надо понять всё и осознать.

Такой код выглядит примерно вот так.

summator = logger('log.txt')(summator)

Использование нескольких декораторов

Создадим два модификатора, которые добавляют html-теги.

def bold(func):
    def wrapped():
        return "<b>" + func() + "</b>"
    return wrapped


def italic(func):
    def wrapped():
        return "<i>" + func() + "</i>"
    return wrapped


@bold
@italic
def hello():
    return "hello world"

print(hello())

На выхлопе ловим такое.

<b><i>hello world</i></b>

Здесь важно запомнить порядок выполнения декораторов. В данном примере сначала выполнится italic, а потом только bold. То есть получается такой вид.

hello = bold(italic(hello))

В итоге, мы имеем еще одну мощную конструкцию, которую применяют практически везде.

Report Page