Декораторы
@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))
В итоге, мы имеем еще одну мощную конструкцию, которую применяют практически везде.