Декораторы в Python и примеры их практического использования. Итак, что же это такое? Для того, чтобы понять, как работают декораторы, в первую очередь следует вспомнить, что функции в python являются объектами, соответственно, их можно возвращать из другой функции или передавать в качестве аргумента. Также следует помнить, что функция в python может быть определена и внутри другой функции. Вспомнив это, можно смело переходить к декораторам. Декораторы — это, по сути, "обёртки", которые дают нам возможность изменить поведение функции, не изменяя её код. Создадим свой декоратор "вручную":
>>> def my_shiny_new_decorator(function_to_decorate):
... # Внутри себя декоратор определяет функцию-"обёртку". Она будет обёрнута вокруг декорируемой,
... # получая возможность исполнять произвольный код до и после неё.
... def the_wrapper_around_the_original_function():
... print("Я - код, который отработает до вызова функции")
... function_to_decorate() # Сама функция
... print("А я - код, срабатывающий после")
... # Вернём эту функцию
... return the_wrapper_around_the_original_function
...
>>> # Представим теперь, что у нас есть функция, которую мы не планируем больше трогать.
>>> def stand_alone_function():
... print("Я простая одинокая функция, ты ведь не посмеешь меня изменять?")
...
>>> stand_alone_function()
Я простая одинокая функция, ты ведь не посмеешь меня изменять?
>>> # Однако, чтобы изменить её поведение, мы можем декорировать её, то есть просто передать декоратору,
>>> # который обернет исходную функцию в любой код, который нам потребуется, и вернёт новую,
>>> # готовую к использованию функцию:
>>> stand_alone_function_decorated = my_shiny_new_decorator(stand_alone_function)
>>> stand_alone_function_decorated()
Я - код, который отработает до вызова функции
Я простая одинокая функция, ты ведь не посмеешь меня изменять?
А я - код, срабатывающий после
Наверное, теперь мы бы хотели, чтобы каждый раз, во время вызова stand_alone_function, вместо неё вызывалась stand_alone_function_decorated. Для этого просто перезапишем stand_alone_function:
>>> stand_alone_function = my_shiny_new_decorator(stand_alone_function)
>>> stand_alone_function()
Я - код, который отработает до вызова функции
Я простая одинокая функция, ты ведь не посмеешь меня изменять?
А я - код, срабатывающий после
Собственно, это и есть декораторы. Вот так можно было записать предыдущий пример, используя синтаксис декораторов:
>>> @my_shiny_new_decorator
... def another_stand_alone_function():
... print("Оставь меня в покое")
...
>>> another_stand_alone_function()
Я - код, который отработает до вызова функции
Оставь меня в покое
А я - код, срабатывающий после
То есть, декораторы в python — это просто синтаксический сахар для конструкций вида: another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function) При этом, естественно, можно использовать несколько декораторов для одной функции, например так:
>>> def bread(func):
... def wrapper():
... print()
... func()
... print("<\______/>")
... return wrapper
...
>>> def ingredients(func):
... def wrapper():
... print("#помидоры#")
... func()
... print("~салат~")
... return wrapper
...
>>> def sandwich(food="--ветчина--"):
... print(food)
...
>>> sandwich()
--ветчина--
>>> sandwich = bread(ingredients(sandwich))
>>> sandwich()
#помидоры#
--ветчина--
~салат~
<\______/>
И используя синтаксис декораторов:
>>> @bread
... @ingredients
... def sandwich(food="--ветчина--"):
... print(food)
...
>>> sandwich()
#помидоры#
--ветчина--
~салат~
<\______/>
Также нужно помнить о том, что важен порядок декорирования. Сравните с предыдущим примером:
>>> @ingredients
... @bread
... def sandwich(food="--ветчина--"):
... print(food)
...
>>> sandwich()
#помидоры#
--ветчина--
<\______/>
~салат~
Передача декоратором аргументов в функциюОднако, все декораторы, которые мы рассматривали, не имели одного очень важного функционала — передачи аргументов декорируемой функции. Собственно, это тоже несложно сделать.
>>> def a_decorator_passing_arguments(function_to_decorate):
... def a_wrapper_accepting_arguments(arg1, arg2):
... print("Смотри, что я получил:", arg1, arg2)
... function_to_decorate(arg1, arg2)
... return a_wrapper_accepting_arguments
...
>>> # Теперь, когда мы вызываем функцию, которую возвращает декоратор, мы вызываем её "обёртку",
>>> # передаём ей аргументы и уже в свою очередь она передаёт их декорируемой функции
>>> @a_decorator_passing_arguments
... def print_full_name(first_name, last_name):
... print("Меня зовут", first_name, last_name)
...
>>> print_full_name("Vasya", "Pupkin")
Смотри, что я получил: Vasya Pupkin
Меня зовут Vasya Pupkin
Декорирование методовОдин из важных фактов, которые следует понимать, заключается в том, что функции и методы в Python — это практически одно и то же, за исключением того, что методы всегда ожидают первым параметром ссылку на сам объект (self). Это значит, что мы можем создавать декораторы для методов точно так же, как и для функций, просто не забывая про self.
>>> def method_friendly_decorator(method_to_decorate):
... def wrapper(self, lie):
... lie -= 3
... return method_to_decorate(self, lie)
... return wrapper
...
>>> class Lucy:
... def __init__(self):
... self.age = 32
... @method_friendly_decorator
... def sayYourAge(self, lie):
... print("Мне {} лет, а ты бы сколько дал?".format(self.age + lie))
...
>>> l = Lucy()
>>> l.sayYourAge(-3)
Мне 26 лет, а ты бы сколько дал?
Конечно, если мы создаём максимально общий декоратор и хотим, чтобы его можно было применить к любой функции или методу, то можно воспользоваться распаковкой аргументов:
>>> def a_decorator_passing_arbitrary_arguments(function_to_decorate):
... # Данная "обёртка" принимает любые аргументы
... def a_wrapper_accepting_arbitrary_arguments(*args, **kwargs):
... print("Передали ли мне что-нибудь?:")
... print(args)
... print(kwargs)
... function_to_decorate(*args, **kwargs)
... return a_wrapper_accepting_arbitrary_arguments
...
>>> @a_decorator_passing_arbitrary_arguments
... def function_with_no_argument():
... print("Python is cool, no argument here.")
...
>>> function_with_no_argument()
Передали ли мне что-нибудь?:
()
{}
Python is cool, no argument here.
>>> @a_decorator_passing_arbitrary_arguments
... def function_with_arguments(a, b, c):
... print(a, b, c)
...
>>> function_with_arguments(1, 2, 3)
Передали ли мне что-нибудь?:
(1, 2, 3)
{}
1 2 3
>>> @a_decorator_passing_arbitrary_arguments
... def function_with_named_arguments(a, b, c, platypus="Почему нет?"):
... print("Любят ли {}, {} и {} утконосов? {}".format(a, b, c, platypus))
...
>>> function_with_named_arguments("Билл", "Линус", "Стив", platypus="Определенно!")
Передали ли мне что-нибудь?:
('Билл', 'Линус', 'Стив')
{'platypus': 'Определенно!'}
Любят ли Билл, Линус и Стив утконосов? Определенно!
>>> class Mary(object):
... def __init__(self):
... self.age = 31
... @a_decorator_passing_arbitrary_arguments
... def sayYourAge(self, lie=-3): # Теперь мы можем указать значение по умолчанию
... print("Мне {} лет, а ты бы сколько дал?".format(self.age + lie))
...
>>> m = Mary()
>>> m.sayYourAge()
Передали ли мне что-нибудь?:
(<__main__.Mary object at 0x7f6373017780>,)
{}
Мне 28 лет, а ты бы сколько дал?
| |
| |
Просмотров: 373 | |
Всего комментариев: 0 | |