Для чего используются декораторы в python
Декораторы в Python представляют функцию, которая в качестве параметра получает функцию и в качестве результата также возвращает функцию. Декораторы позволяют модифицировать выполняемую функцию, значения ее параметров и ее результат без изменения исходного кода этой функции.
Рассмотрим простейший пример:
# определение функции декоратора def select(input_func): def output_func(): # определяем функцию, которая будет выполняться вместо оригинальной print("*****************") # перед выводом оригинальной функции выводим всякую звездочки input_func() # вызов оригинальной функции print("*****************") # после вывода оригинальной функции выводим всякую звездочки return output_func # возвращаем новую функцию # определение оригинальной функции @select # применение декоратора select def hello(): print("Hello METANIT.COM") # вызов оригинальной функции hello()
Вначале определяется собственно функция декоратора, которая в данном случае называется select() . В качестве параметра декоратор получает функцию (в данном случае параметр input_func ), к которой этот декоратор будет применяться:
def select(input_func): def output_func(): # определяем функцию, которая будет выполняться вместо оригинальной print("*****************") # перед выводом оригинальной функции выводим всякую звездочки input_func() # вызов оригинальной функции print("*****************") # после вывода оригинальной функции выводим всякую звездочки return output_func # возвращаем новую функцию
Результатом декоратора в данном случае является локальная функция output_func , в которой вызывается входная функция input_func. Для простоты здесь перед и после вызыва input_func для красоты просто выводим набор символов «#».
Далее определяется стандартная функция, к которой применяется декоратор — в данном случае это функция hello , которая просто выводит на консоль некоторую строку:
@select # применение декоратора select def hello(): print("Hello METANIT.COM")
Для применения декоратора перед определением функции указывается символ @ , после которого идет имя декоратора. То есть в данном случае к функции hello() применяется декоратор select().
Далее вызываем обычную функцию:
hello()
Поскольку к этой функции применяется декоратор select, то в результате функциия hello передается в декоратор select() в качестве параметра input_func . И поскольку декоратор возвращает новую функцию — output_func, то фактически в данном случае будет выполняться именно эта функция output_func()
В итоге мы получим следующий консольный вывод:
***************** Hello METANIT.COM *****************
Получение параметров функции в декораторе
Декоратор может перехватывать передаваемые в функцию аргументы:
# определение функции декоратора def check(input_func): def output_func(*args): # через *args получаем значения параметров оригинальной функции input_func(*args) # вызов оригинальной функции return output_func # возвращаем новую функцию # определение оригинальной функции @check def print_person(name, age): print(f"Name: Age: ") # вызов оригинальной функции print_person("Tom", 38)
Здесь функция print_person() принимает два параметра: name (имя) и age (возраст). К этой функции применяется декоратор check()
В декораторе check возвращается локальная функция output_func() , которая принимает некоторый набор значений в виде параметра *args — это те значения, которые передаются в оригинальную функцию, к которой применяется декоратор. То есть в данном случае *args будет содержать значения параметров name и age.
def check(input_func): def output_func(*args): # через *args получаем значения параметров функции input_func
Здесь просто передаем эти значения в оригинальную функцию:
input_func(*args)
В итоге в данном получим следующий консольный вывод
Name: Tom Age: 38
Но что, если в функцию print_person будет передано какое-то недопустимое значение, например, отрицательный возраст? Одним из преимуществ декораторов как раз является то, что мы можем проверить и при необходимости модифицировать значения параметров. Например:
# определение функции декоратора def check(input_func): def output_func(*args): name = args[0] age = args[1] # получаем значение второго параметра if age < 0: age = 1 # если возраст отрицательный, изменяем его значение на 1 input_func(name, age) # передаем функции значения для параметров return output_func # определение оригинальной функции @check def print_person(name, age): print(f"Name: Age: ") # вызов оригинальной функции print_person("Tom", 38) print_person("Bob", -5)
args фактически представляет набор значений, и, используя индексы, мы можем получить значения параметров по позиции и что-то с ними сделать. Так, здесь, если значение возраста меньше 0, то устанавливаем 1. Затем передаем эти значения в вызов функции. В итоге здесь получим следующий вывод:
Name: Tom Age: 38 Name: Bob Age: 1
Получение результата функции
Подобным образом можно получить результат функции и при необходимости изменить его:
# определение функции декоратора def check(input_func): def output_func(*args): result = input_func(*args) # передаем функции значения для параметров if result < 0: result = 0 # если результат функции меньше нуля, то возвращаем 0 return result return output_func # определение оригинальной функции @check def sum(a, b): return a + b # вызов оригинальной функции result1 = sum(10, 20) print(result1) # 30 result2 = sum(10, -20) print(result2) # 0
Здесь определена функция sum() , которая возвращает сумму чисел. В декораторе check проверяем результат функции и для простоты, если он меньше нуля, то возвращаем 0.
Консольный вывод программы:
Для чего нужны декораторы и как их использовать?
Функции и классы в Python – объекты удобные. Они сами могут быть входящими и исходящими аргументами функций. Это свойство можно использовать, чтобы сделать жизнь разработчика немного приятнее.
Смысл паттерна Декоратор заключается в том, что некоторая функция заворачивается в другую функцию, приобретая от нее новые возможности. Например, так можно вести логи, вводить пред- и постусловия, добавлять методы для классов.
Декораторы в Python – это, по сути, синтаксический сахар. Для их обозначения используется символ @ .
Код ниже описывает обычный вариант приготовления сэндвича. Нужно взять что-нибудь мясное, обложить с двух сторон овощами и положить в булку.
def bread(func): def wrapper(): print "''''''\>" func() print "<\______/>" return wrapper def vegetables(func): def wrapper(): print "#помидорка#" func() print "~лист салата~" return wrapper def sandwich(food="--ветчина--"): print food sandwich = bread(vegetables(sandwich)) sandwich() #''''''\> # #помидорка# # --ветчина-- # ~лист салата~ #<\______/>
Изначально функция sandwich только печатает начинку, а затем она становится полноценным бутербродом. То же самое можно сделать чуть проще:
@bread @vegetables def sandwich(food="--ветчина--"): print food sandwich()
Вместо привычного синтаксиса вызова функции используются два декоратора, которые оборачивают исходный сэндвич. Название остается прежним, но результат работы уже другой.
В Python есть несколько встроенных декораторов, например, @classmethod , @staticmethod , @property .
Декораторы
Декораторы в Python и примеры их практического использования.
Итак, что же это такое? Для того, чтобы понять, как работают декораторы, в первую очередь следует вспомнить, что функции в python являются объектами, соответственно, их можно возвращать из другой функции или передавать в качестве аргумента. Также следует помнить, что функция в python может быть определена и внутри другой функции.
Вспомнив это, можно смело переходить к декораторам. Декораторы — это, по сути, "обёртки", которые дают нам возможность изменить поведение функции, не изменяя её код.
Создадим свой декоратор "вручную":
Наверное, теперь мы бы хотели, чтобы каждый раз, во время вызова stand_alone_function, вместо неё вызывалась stand_alone_function_decorated. Для этого просто перезапишем stand_alone_function:
Собственно, это и есть декораторы. Вот так можно было записать предыдущий пример, используя синтаксис декораторов:
То есть, декораторы в python — это просто синтаксический сахар для конструкций вида:
При этом, естественно, можно использовать несколько декораторов для одной функции, например так:
Однако, все декораторы, которые мы рассматривали, не имели одного очень важного функционала — передачи аргументов декорируемой функции. Собственно, это тоже несложно сделать.
Декорирование методов
Один из важных фактов, которые следует понимать, заключается в том, что функции и методы в Python — это практически одно и то же, за исключением того, что методы всегда ожидают первым параметром ссылку на сам объект (self). Это значит, что мы можем создавать декораторы для методов точно так же, как и для функций, просто не забывая про self.
Конечно, если мы создаём максимально общий декоратор и хотим, чтобы его можно было применить к любой функции или методу, то можно воспользоваться распаковкой аргументов:
Python is cool, no argument here. 1 2 3 Любят ли Билл, Линус и Стив утконосов? Определенно! ,) <> Мне 28 лет, а ты бы сколько дал?
Декораторы с аргументами
А теперь попробуем написать декоратор, принимающий аргументы:
Теперь перепишем данный код с помощью декораторов:
Вернёмся к аргументам декораторов, ведь, если мы используем функцию, чтобы создавать декораторы "на лету", мы можем передавать ей любые аргументы, верно?
Таким образом, мы можем передавать декоратору любые аргументы, как обычной функции. Мы можем использовать и распаковку через *args и **kwargs в случае необходимости.
Некоторые особенности работы с декораторами
- Декораторы несколько замедляют вызов функции, не забывайте об этом.
- Вы не можете "раздекорировать" функцию. Безусловно, существуют трюки, позволяющие создать декоратор, который можно отсоединить от функции, но это плохая практика. Правильнее будет запомнить, что если функция декорирована — это не отменить.
- Декораторы оборачивают функции, что может затруднить отладку.
Последняя проблема частично решена добавлением в модуле functools функции functools.wraps, копирующей всю информацию об оборачиваемой функции (её имя, из какого она модуля, её документацию и т.п.) в функцию-обёртку.
Забавным фактом является то, что functools.wraps тоже является декоратором.
Декораторы могут быть использованы для расширения возможностей функций из сторонних библиотек (код которых мы не можем изменять), или для упрощения отладки (мы не хотим изменять код, который ещё не устоялся).
Также полезно использовать декораторы для расширения различных функций одним и тем же кодом, без повторного его переписывания каждый раз, например:
wrapper 0.00011799999999997923 арозА упал ан алапу азор А wrapper 0.00017800000000001148 !amanaP :lanac a ,noep a ,stah eros ,raj a,hsac ,oloR a ,tur a ,mapS ,snip ,eperc a , .
Для вставки кода на Python в комментарий заключайте его в теги
Декораторы — Python: Функции
В программировании часто возникают задачи, когда нужно добавить поведение к уже существующим функциям или классам. Например, логирование, проверку входных данных или замер времени выполнения функции. В таких случаях использование декораторов может значительно упростить решение задачи.
В этом уроке мы рассмотрим, что такое декораторы и как их использовать, чтобы добавлять дополнительную функциональность к существующим функциям.
Что такое декораторы
Декораторы позволяют динамически изменять поведение функций и классов с помощью добавления или изменения их функциональности без изменения самого кода.
Декораторы в Python — это функции, которые принимают другую функцию в качестве аргумента, добавляют к ней некоторую дополнительную функциональность и возвращают функцию с измененным поведением.
Декораторы используются, чтобы изменять работу существующих функций или классов, добавлять новые возможности и обеспечивать безопасность.
Предположим, что у нас есть функция, которая выполняет математические операции и возвращает результат:
add_numbers(x, y): return x + ydef
Мы можем создать декоратор, который добавит к этой функции функциональность для отладки:
debug_decorator(func): def wrapper(*args, **kwargs): print("Вызов функции:", func.__name__) print("Аргументы:", args, kwargs) result = func(*args, **kwargs) print("Результат:", result) return result return wrapperdef
Этот декоратор принимает функцию в качестве аргумента и возвращает новую функцию-обертку, которая добавляет отладочные сообщения в процесс выполнения исходной функции. Чтобы применить этот декоратор к функции add_numbers , мы можем вызвать эту функцию с аргументом:
@debug_decorator def add_numbers(x, y): return x + y
Теперь функция add_numbers будет выполняться с дополнительными отладочными сообщениями:
add_numbers(2, 3) # Вызов функции: add_numbers # Аргументы: (2, 3) <> # Результат: 5
Также мы можем создавать несколько декораторов для одной функции, которые будут применяться последовательно:
@debug_decorator @time_decorator def add_numbers(x, y): return x + y
В этом примере сначала применится декоратор времени выполнения, а затем отладочный декоратор.
Как связаны декораторы и замыкания
Внутренняя функция wrapper декоратора обычно ссылается на переменные из внешней функции, что создает замыкание. В примере с декоратором debug_decorator функция wrapper ссылается на func , которая была определена во внешней функции debug_decorator . Это позволяет декоратору использовать и изменять переменные из внешней функции в рамках своей логики работы, что делает декораторы более гибкими инструментами.
Также декораторы могут использовать замыкания, чтобы сохранять состояния между вызовами функции. Например, декоратор может создать замыкание, которое сохраняет информацию о том, сколько раз функция была вызвана, и возвращать это значение при каждом вызове функции.
Вот пример декоратора, который использует замыкание, чтобы отслеживать количество вызовов функции:
def count_calls(func): num_calls = 0 def wrapper(*args, **kwargs): nonlocal num_calls num_calls += 1 print(f"Функция была вызвана num_calls> раз(а)") return func(*args, **kwargs) return wrapper @count_calls def my_func(): print("Hello, world!") my_func() # Функция была вызвана 1 раз(а) # Hello, world! my_func() # Функция была вызвана 2 раз(а) # Hello, world!
В этом примере декоратор count_calls принимает функцию func и возвращает новую функцию wrapper . Последняя отслеживает количество вызовов func и выводит сообщение при каждом вызове. Затем декоратор применяется к функции my_func . Каждый раз, когда my_func вызывается, декоратор увеличивает счетчик вызовов и выводит сообщение.
Рассмотрим еще один пример, где создадим декоратор, который измеряет время выполнения функции:
import time def timer(func): def wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print(f"Время выполнения функции func.__name__>: end_time - start_time> сек.") return result return wrapper @timer def some_function(): time.sleep(2) some_function() # Время выполнения функции some_function: 2.000108242034912 сек.
Здесь мы определяем декоратор timer , который принимает функцию func и возвращает функцию-обертку wrapper . Функция-обертка вызывает функцию func , замеряет время ее выполнения и сохраняет результат работы func в переменную result . Затем функция-обертка выводит время выполнения функции на экран и возвращает результат вызова функции.
В конце применяем декоратор timer к функции some_function . Когда мы вызываем функцию some_function , декоратор автоматически вызывается и выводит время выполнения функции на экран.
Выводы
Существует большое количество готовых декораторов, доступных в стандартной библиотеке Python и других библиотеках. Некоторые из них позволяют кэшировать результаты функций, обеспечивать авторизацию и безопасность, профилировать код, проверять типы данных и многое другое.
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях: