На каждом собеседовании спрашивают про декораторы. Что это? В чём прикол?
Олег
Скорее всего Вы уже видели декораторы в коде python — это те строки, что начинаются с @
. Декораторы позволяют динамически изменять поведение или расширять функциональность существующих функций без изменения самой функции.
@my_decorator
def func(arg):
# make some magic with arg
return 'hello'
Декораторы в python используют замыкания, простейший пример которого выглядит вот так:
def make_printer(string):
def inner():
print(string)
return inner
>>> make_hello = make_printer('hello!')
>>> make_hello()
>>> hello!
В этом примере make_printer
создаёт функцию, которая выводит строку. Новой функции мы присвоили переменную make_hello
. Внутренняя фукнция inner
является замыканием.
Попробуем написать простейшую функцию, которая выводит текст, который ей передали:
def print_text(string):
print(string)
>>> print_text('hello')
hello
Исходя из примера выше напишем декоратор, который делает вывод любой функции строчными буквами:
def make_string_upper(func):
def inner(string):
func(string.upper())
return inner
>>> print_upper_string = make_string_upper(print_text)
>>> print_upper_string('hello')
HELLO
Вот так просто мы декорировали обычную функцию. Для того, чтобы это выглядело более приятно, мы можем применить синтаксический сахар
и переписать это всё таким образом:
def make_string_upper(func):
def inner(string):
func(string.upper())
return inner
@make_string_upper
def print_text(string):
print(string)
В этом примере мы передаём декорированной функции один аргумент string
. Передавать можно любые аргументы, как в обычной фукнции. Попробуем добавить немного функциональности нашему декоратору:
def make_string_upper(func):
def inner(string):
print('BEFORE')
func(string.upper())
print('AFTER')
return inner
@make_string_upper
def print_text(string):
print(string)
>>> print_text('hello')
BEFORE
HELLO
AFTER
>>> print(print_text.__name__))
inner
Так как в декораторе мы возвращаем inner
, то и имя функции становится inner
. Если имя функции нам важно, то нужно перезаписать его в нашем декораторе:
def make_string_upper(func):
def inner(string):
func(string.upper())
inner.__name__ = func.__name__
return inner
@make_string_upper
def print_text(string):
print(string)
>>> print(print_text.__name__)
print_text
Если функцию, которыю мы хотим декорировать, нужно выполнять только при определённых условиях, можно передать это условие в параметрах:
def if_seen(conditional, message):
def dec(wrapped):
def inner(*args, **kwargs):
if not conditional:
return wrapped(*args, **kwargs)
else:
print(message)
return inner
return dec
@if_seen(True, 'I have seen you today')
def make_hello():
print('Oh Hello!')
>>> make_hello()
I have seen you today
Вернуть inner
сразу нельзя, потому что получим бесконечную рекурсию.
Также декораторы можно комбинировать, пример обёртки html тэгами:
def wrap_div(func):
def inner(string):
print('<div>', end='')
func(string)
print('</div>')
return inner
def wrap_p(func):
def inner(string):
print('<p>', end='')
func(string)
print('</p>', end='')
return inner
@wrap_div
@wrap_p
def wrapped_text(string):
print(string, end='')
def another_wrapped_text(string):
print(string, end='')
>>> wrapped_text('Text wrapped out with html tags')
<div><p>Text wrapped out with html tags</p></div>
>>> without_syntax_sugar = wrap_div(wrap_p(another_wrapped_text))
>>> without_syntax_sugar('decorated too')
<div><p>decorated too</p></div>
Вот так просто можно использовать декораторы в python.
Попробуйте бесплатные уроки по Python
Получите крутое код-ревью от практикующих программистов с разбором ошибок и рекомендациями, на что обратить внимание — бесплатно.
Переходите на страницу учебных модулей «Девмана» и выбирайте тему.