Уроки Питон №15/19. Декораторы функций (@simple_decore) в Питон

Урок 19. Декораторы функций в Python

Этот урок посвящен теме декораторов в Python. Большое внимание уделено свойствам функций в Python, на базе которых реализована идея декораторов. Рассмотрены декораторы принимающие аргументы и возвращающие значение из функции.

Что нужно знать о функциях в Python?

Для начала разберем два аспекта связанные с функциями в Python. Во-первых: функция – это объект специального вида, поэтому ее можно передавать в качестве аргумента другим функциям. Во-вторых: внутри функций можно создавать другие функции, вызывать их и возвращать как результат через return. Остановимся на этих моментах более подробно.

Функция как объект

В Python передача одной функции в качестве аргумента другой функции – это нормальная практика. Например, если у вас есть список целых чисел, и вы хотите на базе него получить другой список, элементами которого будут квадраты первого, то такую задачу можно решить в одну строчку.

>>> # исходный список

>>> a = [1, 2, 3, 4, 5]

>>> # функция, возводящая переданное ей число в квадрат

>>> sq = lambda x: x**2

>>> # проверим ее работу

>>> print(sq(5))

25

>>> # получаем список квадратов

>>> b = list(map(sq, a))

>>> print(b)

[1, 4, 9, 16, 25]

Здесь мы передали функции map в качестве первого аргумента функцию sq, которая будет применяться по очереди ко всем элементам списка a.

В Python функция – это специальный объект, который имеет метод __call__(). Если мы создадим вот такой класс.

class DemoCall():

def __call__(self):

return "Hello!"

То объект такого класса можно вызывать как функцию.

>>> hello = DemoCall()

>>> hello()

'Hello!'

Функция внутри функции

Вторым важным свойством функции, для понимания темы декораторов, является то, что их можно создавать, вызывать и возвращать из других функций. На этом построена идея замыкания (closures).

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

def mul(a):

def helper(b):

return a * b

return helper

В ней реализованы два важных свойства которые нам понадобятся: внутри функции mul() создается еще одна функция, которая называется helper(); функция mul() возвращает функцию helper как результат работы.

Вызывается эта функция так:

>>>mul(4)(2)

8

Ее главная фишка состоит в том, что можно создавать на базе функции mul() свои кастомизированные функции. Например, создадим функцию “умножение на три”.

>>>three_mul = mul(3)

>>>three_mul(5)

15

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

Что такое декоратор функции в Python?

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

Создание декоратора

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

def first_test():

print("Test function 1")

def second_test():

print("Test function 2")

Мы хотим дополнить их так, чтобы перед вызовом основного кода функции печаталась строка “Run function”, а по окончании – “Stop function”.

Сделать это можно двумя способами. Первый – это добавить указанные строки в начало в конец каждой функции, но это не очень удобно, т.к. если мы захотим убрать это, нам придется снова модифицировать тело функции. А если они написаны не нами, либо являются частью общей кодовой базы проекта, сделать это будет уже не так просто. Второй вариант – это воспользоваться знаниями из раздела “Что нужно знать о функциях в Python?”

Создадим вот такую функцию.

def simple_decore(fn):

def wrapper():

print("Run function")

fn()

print("Stop function")

return wrapper

Обернем наши функции в эту оболочку.

first_test_wrapped = simple_decore(first_test)

second_test_wrapped = simple_decore(second_test)

Функции first_test и second_test остались неизменными.

>>> first_test()

Test function 1

>>> second_test()

Test function 2

Функции first_test_wrapped и second_test_wrapped  обладают функционалом, которого мы добивались.

>>> first_test_wrapped()

Run function

Test function 1

Stop function

>>> first_test_wrapped()

Run function

Test function 1

Stop function

Если необходимо, чтобы так работали функций с именами first_test и second_test, то  можно сделать так.

first_test = first_test_wrapped

second_test = second_test_wrapped

Проверим это.

>>> first_test()

Run function

Test function 1

Stop function

>>> second_test()

Run function

Test function 2

Stop function

То, что мы только что сделали и является реализацией идеи декоратора. Но вместо строк:

def first_test():

print("Test function 1")

first_test_wrapped = simple_decore(first_test)

first_test = first_test_wrapped

Можно написать вот так:

@simple_decore

def first_test():

print("Test function 1")

@simple_decore – это и есть декоратор функции.

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

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

def param_transfer(fn):

def wrapper(arg):

print("Run function: " + str(fn.__name__) + "(), with param: " + str(arg))

fn(arg)

return wrapper

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

@param_transfer

def print_sqrt(num):

print(num**0.5)

Выполним эту функцию с аргументом 4.

>>> print_sqrt(4)

Run function: print_sqrt(), with param: 4

2.0

Декораторы для методов класса

Методы классов также можно объявлять с декоратором. Модифицируем декоратор param_transfer.

def method_decor(fn):

def wrapper(self):

print("Run method: " + str(fn.__name__))

fn(self)

return wrapper

Создадим класс для представления двумерного вектора (из математики). В этом классе определим метод norm(), который выводит модуль вектора.

class Vector():

def __init__(self, px = 0, py = 0):

self.px = px

self.py = py

@method_decor

def norm(self):

print((self.px**2 + self.py**2)**0.5)

Продемонстрируем работу этого метода.

>>> vc = Vector(px=10, py=5)

>>> vc.norm()

Run method: norm

11.180339887498949

Возврат результата работы функции через декоратор

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

def decor_with_return(fn):

def wrapper(*args, **kwargs):

print("Run method: " + str(fn.__name__))

return fn(*args, **kwargs)

return wrapper

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

@decor_with_return

def calc_sqrt(val):

return val**0.5

Выполним функцию calc_sqrt с параметром 16.

>>> tmp = calc_sqrt(16)

Run method: calc_sqrt

>>> print(tmp)

4.0

Популярные репетиторы:

Рейтинг 5 из 5: 45 отзывов
 
C самого истока своего продвижения по службе, когда еще учился в аспирантуре, я мечтал собрать вместе два моих основных интересов: Математику, Информатику и Обучение.

Умелый математик для школьников и студентов, кандидат физико математических наук, докторант, педагогический стаж более 16 лет, экстренно   подготовит к вступительному экзамену ЕГЭ по математике на 1 курс с помощью интересных методов по усовершенствованию памяти и ускорению мышления. 

Участвует в ведущих академических симпозиумах ACL, ICCV и ECCV . Впечатляюще поработал по науке в стартапе по Information Retrieval и Big Data. Без труда программирует на Python, Ruby и Node. Консультации по математическим программам MathLab, Sage и MathCad .

Более 320 учащихся  поступили «на бюджет» в ВУЗы Москвы: МГУ, МАИ, Школа Анализа Данных Яндекса и МЭИ и т.д.. Опыт преподавателя по математике для абитуриентов более 20 лет. Занятия ведутся Локально в Москве м. Китай-город и дистанционно по Google Hangout. Speaks to English.

Запись на занятия

Ваше сообщение отправлено