Декораторы. Примеры их практического использования. Функции в Python

Содержание

Слайд 2

функции в python являются объектами их можно возвращать из другой функции

функции в python являются объектами

их можно возвращать из другой функции или
передавать

в качестве аргумента.
следует помнить, что
функция в python может быть
определена и
внутри другой функции.
Слайд 3

Декораторы — это "обёртки", которые дают нам возможность изменить поведение функции, не изменяя её код.

Декораторы 

 — это "обёртки",
которые дают нам возможность
изменить поведение функции,
не

изменяя её код.
Слайд 4

Создадим свой декоратор "вручную":

Создадим

свой декоратор "вручную":

Слайд 5

>>> def my_shiny_new_decorator(function_to_decorate): ... # Внутри себя декоратор определяет функцию-"обёртку". Она

>>> def my_shiny_new_decorator(function_to_decorate):
...
# Внутри себя декоратор определяет функцию-"обёртку". Она

будет обёрнута вокруг декорируемой,
... # получая возможность исполнять произвольный код до и после неё.
Слайд 6

... def the_wrapper_around_the_original_function(): ... print("Я - код, который отработает до вызова

... def the_wrapper_around_the_original_function():
... print("Я - код, который отработает до вызова

функции")
... function_to_decorate()
# Сама функция
... print("А я - код, срабатывающий после")
Слайд 7

... # Вернём эту функцию ... return the_wrapper_around_the_original_function ... >>> #

... # Вернём эту функцию
... return the_wrapper_around_the_original_function
... >>>
#

Представим теперь,
что у нас есть функция, которую мы
не планируем больше трогать.
Слайд 8

>>> def stand_alone_function(): ... print("Я простая одинокая функция, ты ведь не посмеешь меня изменять?")

>>> def stand_alone_function():
... print("Я простая одинокая функция, ты ведь не

посмеешь меня изменять?")
Слайд 9

>>> # Однако, чтобы изменить её поведение, мы можем декорировать её,

>>> # Однако, чтобы изменить её поведение, мы можем декорировать её,

то есть просто передать декоратору,
>>> # который обернет исходную функцию в любой код, который нам потребуется, и вернёт новую,
>>> # готовую к использованию функцию:
Слайд 10

>>> stand_alone_function_decorated = my_shiny_new_decorator(stand_alone_function) >>> stand_alone_function_decorated() Я - код, который отработает

>>> stand_alone_function_decorated = my_shiny_new_decorator(stand_alone_function)
>>> stand_alone_function_decorated()
Я - код, который отработает

до вызова функции Я простая одинокая функция, ты ведь не посмеешь меня изменять? А я - код, срабатывающий после
Слайд 11

мы бы хотели, чтобы каждый раз, во время вызова stand_alone_function, вместо

мы бы хотели, чтобы каждый раз, во время вызова stand_alone_function, вместо

неё вызывалась stand_alone_function_decorated. Для этого просто перезапишем stand_alone_function:
Слайд 12

>>>>>> stand_alone_function = my_shiny_new_decorator(stand_alone_function) >>> stand_alone_function() Я - код, который отработает

>>>>>> stand_alone_function = my_shiny_new_decorator(stand_alone_function)
>>> stand_alone_function()
Я - код, который отработает

до вызова функции
Я простая одинокая функция,
ты ведь не посмеешь меня изменять?
А я - код, срабатывающий после
Слайд 13

это и есть декораторы Вот так можно было записать предыдущий пример, используя синтаксис декораторов:

это и есть декораторы

Вот так можно было записать предыдущий пример,
используя


синтаксис декораторов:
Слайд 14

>>> @my_shiny_new_decorator ... def another_stand_alone_function(): ... print("Оставь меня в покое") ...

>>> @my_shiny_new_decorator
... def another_stand_alone_function():
... print("Оставь меня в покое")
...

>>> another_stand_alone_function()
Я - код, который отработает до вызова функции
Оставь меня в покое
А я - код, срабатывающий после
Слайд 15

декораторы в python — это просто синтаксический сахар для конструкций вида: another_stand_alone_function = my_shiny_new_decorator(another_stand_alone_function)

декораторы в python

— это просто
синтаксический сахар
для конструкций вида:
another_stand_alone_function

= my_shiny_new_decorator(another_stand_alone_function)
Слайд 16

При этом, естественно, можно использовать несколько декораторов для одной функции, например так:

При этом, естественно,
можно использовать
несколько декораторов
для одной функции,
например

так:
Слайд 17

>>> def bread(func): ... def wrapper(): ... print() ... func() ...

>>> def bread(func):
... def wrapper():
... print()
... func()
...

print("<\______/>")
... return wrapper ... >>>
Слайд 18

def ingredients(func): ... def wrapper(): ... print("#помидоры#") ... func() ... print("~салат~«

def ingredients(func):
... def wrapper():
... print("#помидоры#")
... func()
...

print("~салат~«
) ... return wrapper ... >>>
Слайд 19

def sandwich(food="--ветчина--"): >>> sandwich() --ветчина– >>> sandwich = bread(ingredients(sandwich)) >>> sandwich() #помидоры # --ветчина-- ~салат~

def sandwich(food="--ветчина--"):
>>> sandwich()
--ветчина–
>>> sandwich = bread(ingredients(sandwich))
>>> sandwich()


#помидоры
# --ветчина-- ~салат~ <\______/>
Слайд 20

используя синтаксис декораторов: >>> >>> @bread ... @ingredients ... def sandwich(food="--ветчина--"):

используя синтаксис декораторов:

>>>
>>> @bread
... @ingredients
... def sandwich(food="--ветчина--"):
... print(food)


... >>> sandwich()
#помидоры
# --ветчина-- ~салат~ <\______/>
Слайд 21

нужно помнить о том, что важен порядок декорирования.

нужно помнить

о том, что
важен порядок декорирования.

Слайд 22

Сравните с предыдущим примером: >>> >>> @ingredients ... @bread ... def

Сравните с предыдущим примером:

>>>
>>> @ingredients
... @bread
... def sandwich(food="--ветчина--"):
...

print(food)
... >>> sandwich()
#помидоры# --ветчина-- <\______/> ~салат~
Слайд 23

Передача декоратором аргументов в функцию все декораторы, которые мы рассматривали, не

Передача декоратором аргументов в функцию

все декораторы, которые мы рассматривали,
не имели

одного очень важного функционала — передачи аргументов декорируемой функции.
Собственно, это тоже несложно сделать.
Слайд 24

>>>>>> def a_decorator_passing_arguments(function_to_decorate): ... def a_wrapper_accepting_arguments(arg1, arg2): ... print("Смотри, что я

>>>>>> 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
Слайд 25

... >>> # Теперь, когда мы вызываем функцию, которую возвращает декоратор,

... >>> # Теперь, когда мы вызываем функцию, которую возвращает декоратор,

мы вызываем её "обёртку",
>>> # передаём ей аргументы и уже в свою очередь она передаёт их декорируемой функции
Слайд 26

>>> @a_decorator_passing_arguments ... def print_full_name(first_name, last_name): ... print("Меня зовут", first_name, last_name) ... >>> print_full_name("Vasya", "Pupkin«)

>>> @a_decorator_passing_arguments
... def print_full_name(first_name, last_name):
... print("Меня зовут", first_name, last_name)

... >>> print_full_name("Vasya", "Pupkin«)
Слайд 27

Смотрите, что я получил: Vasya Pupkin Меня зовут Vasya Pupkin

Смотрите, что я получил:

Vasya Pupkin
Меня зовут Vasya Pupkin

Слайд 28

... print(food) ... >>> sandwich() --ветчина-- >>> sandwich = bread(ingredients(sandwich)) >>> sandwich() #помидоры# --ветчина-- ~салат~

... print(food) ... >>> sandwich() --ветчина-- >>> sandwich = bread(ingredients(sandwich)) >>>

sandwich() #помидоры# --ветчина-- ~салат~ <\______/>
Слайд 29

Примеры использования декораторов Декораторы могут быть использованы для расширения возможностей функций

Примеры использования декораторов

Декораторы могут быть использованы для расширения возможностей функций из

сторонних библиотек (код которых мы не можем изменять), или для упрощения отладки (мы не хотим изменять код, который ещё не устоялся).
Слайд 30

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

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

же кодом,
без повторного его переписывания
каждый раз,
например:
Слайд 31

>>> >>> def benchmark(func): ... """ ... Декоратор, выводящий время, которое

>>>
>>> def benchmark(func):
... """
... Декоратор, выводящий время, которое заняло

... выполнение декорируемой функции.
... """
Слайд 32

... import time ... def wrapper(*args, **kwargs): ... t = time.clock()

... import time
... def wrapper(*args, **kwargs):
... t = time.clock()

... res = func(*args, **kwargs)
... print(func.__name__, time.clock() - t)
... return res
... return wrapper
Слайд 33

>>> def logging(func): ... """ ... Декоратор, логирующий работу кода. ...

>>> def logging(func): ... """ ... Декоратор, логирующий работу кода. ...

(хорошо, он просто выводит вызовы, но тут могло быть и логирование!) ... """
Слайд 34

... def wrapper(*args, **kwargs): ... res = func(*args, **kwargs) ... print(func.__name__,

... def wrapper(*args, **kwargs): ... res = func(*args, **kwargs) ... print(func.__name__,

args, kwargs) ... return res ... return wrapper
Слайд 35

... >>> def counter(func): ... """ ... Декоратор, считающий и выводящий

... >>> def counter(func): ... """ ... Декоратор, считающий и выводящий

количество вызовов ... декорируемой функции. ... """
Слайд 36

... def wrapper(*args, **kwargs): ... wrapper.count += 1 ... res = func(*args, **kwargs)

... def wrapper(*args, **kwargs): ... wrapper.count += 1 ... res =

func(*args, **kwargs)
Слайд 37

... print("{0} была вызвана: {1}x".format(func.__name__, wrapper.count)) ... return res ... wrapper.count = 0 ... return wrapper

... print("{0} была вызвана: {1}x".format(func.__name__, wrapper.count)) ... return res ... wrapper.count

= 0 ... return wrapper
Слайд 38

>>> @benchmark ... @logging ... @counter ... def reverse_string(string): ... return ''.join(reversed(string))

>>> @benchmark ... @logging ... @counter ... def reverse_string(string): ... return

''.join(reversed(string))
Слайд 39

... >>> print(reverse_string("А роза упала на лапу Азора")) reverse_string была вызвана:

... >>> print(reverse_string("А роза упала на лапу Азора")) reverse_string была вызвана:

1x wrapper ('А роза упала на лапу Азора',) {} wrapper 0.00011799999999997923 арозА упал ан алапу азор А
Слайд 40

>>> print(reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a

>>> print(reverse_string("A man, a plan, a canoe, pasta, heros, rajahs, a

coloratura," ... "maps, snipe, percale, macaroni, a gag, a banana bag, a tan, a tag," ... "a banana bag again (or a camel), a crepe, pins, Spam, a rut, a Rolo, cash
Слайд 41

," ... "a jar, sore hats, a peon, a canal: Panama!"))

," ... "a jar, sore hats, a peon, a canal: Panama!"))


reverse_string была вызвана:
2x wrapper ('A man, a plan, a canoe, pasta, heros, rajahs, a coloratura,maps, snipe, ...',) {}
wrapper 0.00017800000000001148
!amanaP :lanac a ,noep a ,
stah eros ,raj a,hsac ,oloR a ,
tur a ,mapS ,snip ,eperc a ,
...
Слайд 42

ИСПОЛЬЗОВАНИЕ Декораторы сильно облегчают жизнь квалифицированному программисту Резко сокращая необходимый объем

ИСПОЛЬЗОВАНИЕ

Декораторы сильно облегчают жизнь квалифицированному программисту
Резко сокращая необходимый объем требуемой работы

и
Уменьшая число ошибок в программировании
Слайд 43

СПАСОБО ЗА ВНИМАНИЕ

СПАСОБО
ЗА
ВНИМАНИЕ