Что такое декоратор в python
Перейти к содержимому

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

  • автор:

Что такое декоратор в 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

На собеседованиях только и разговоров, что о декораторах. Разбираемся на пальцах, что это.

Цокто Жигмытов

Цокто Жигмытов

Кандидат философских наук, специалист по математическому моделированию. Пишет про Data Science, AI и программирование на Python.

Итак, вы на собеседовании на вакансию джуна-пайтониста. Всё идёт хорошо: вы объяснили про кортежи и списки, про принципы ООП и структуры данных, даже решили небольшую задачку, и вдруг:

— Расскажите, пожалуйста, про декораторы в Python.

Простой ответ

В большинстве случаев будет достаточно сказать своими словами, что такое декоратор, и написать простейший код.

Вслух:

— Декоратор, если в двух словах, это функция, которая добавляет новую функциональность к другой функции без изменения её кода. Он как бы оборачивает, декорирует функцию, тем самым расширяя её возможности.

Пример кода:

Функция say_hi (пример отсюда), которую мы «обернём» в декоратор, возвращает строку «всем привет». Обратите внимание: не печатает, а возвращает.

def say_hi(): return 'всем привет'

А наш декоратор превратит символы этой строки из строчных в прописные. Этой возможности у функции say_hi раньше не было, а теперь будет.

def uppercase_decorator(function): def wrapper(): func = function() make_uppercase = func.upper() return make_uppercase return wrapper

Что здесь происходит — разбираем код построчно:

  • В первой строке мы указываем имя декоратора и то, что он принимает function в качестве своей переменной.
  • Вторая строка — это объявление функции-обёртки wrapper (). Тело обёртки в блоке состоит из трёх строк ниже. Оно как раз и описывает, что именно мы будем делать с функцией, ранее принятой декоратором.
  • Третья строка: записываем входящую переменную function () в локальную переменную func. Здесь «локальная» означает, что она действует только в рамках функции wrapper ().
  • Четвёртая строка: мы применяем к func строковый метод upper и записываем результат в другую локальную переменную make_uppercase.

Больше локальных переменных богу локальных переменных! Конечно, всё это ради понятности и читаемости.

  • Пятая строка: функция wrapper () возвращает переменную make_uppercase, то есть строку от function (), но уже прописными буквами.
  • Последняя строка: декоратор возвращает нам уже саму функцию wrapper, точнее, результат её работы над функцией function.

Как это запустить? Пишем символ @, за ним название декоратора, а объявление функции say_hi переносим на строку под ней:

@uppercase_decorator def say_hi(): return 'всем привет'

Печатаем вывод декорированной функции:

print(say_hi())

Получаем на выходе:

Другие примеры

Если хочется немного дополнить, вот ещё примеры декораторов.

Логирование простых функций

def logging(func): def log_function_called(): print(f'Вызвана ') func() return log_function_called @logging def my_name(): print('Крис') @logging def friends_name(): print('Наруто') my_name() friends_name()

Что здесь происходит:

Декоратор принимает в качестве переменной функцию func. Затем функция-обёртка log_function_called печатает «Вызвана func» и запускает её на выполнение. Функции my_name и friends_name печатают имена Крис и Наруто, а в обёрнутом виде декоратор предваряет их выполнение сообщением об их вызове.

Измерение времени выполнения GET-запроса к серверу

def benchmark(func): import time def wrapper(): start = time.time() func() end = time.time() print('[*] Время выполнения: <> секунд.'.format(end - start)) return wrapper @benchmark def fetch_webpage(): import requests webpage = requests.get('https://skillbox.ru') print(webpage) fetch_webpage()

Что здесь происходит:

В объявлении декоратора benchmark мы импортируем библиотеку time. В функции-обёртке wrapper засекаем время в переменную start, затем выполняем полученную декоратором функцию, а после этого фиксируем время в переменную end. Далее печатаем время выполнения, просто отняв от величины end величину start прямо внутри команды print.

Сама функция fetch_webpage делает простой GET-запрос на наш сайт. Для этого она импортирует популярную библиотеку requests, записывает в переменную webpage ответ сервера Skillbox на запрос и печатает эту переменную.

В обёрнутом виде декоратор «запускает секундомер», функция fetch_webpage делает запрос и печатает ответ, после чего декоратор «выключает секундомер» и печатает время, которое функция потратила на всю эту работу.

Ответы посложнее

Если хотите произвести впечатление, то можно добавить, что:

  • Декоратор — это паттерн проектирования (design pattern) в Python, а также функция второго уровня, то есть принимающая другие функции в качестве переменных и возвращающая их.
  • И в сам декоратор, и в функцию-обёртку можно передать и позиционные, и именованные аргументы — args и kwargs соответственно.
  • Декораторы работают не только с функциями, но и с классами и методами.

Конечно, на собеседовании надо будет пояснить все эти пункты и проиллюстрировать их кодом. На курсе Профессия Python-разработчик вы изучите не только декораторы, но и всё необходимое для того, чтобы с блеском проходить любые собеседования.

Читайте также:

  • Как начать программировать на Python
  • Ох уж эти детки: 10 вундеркиндов из мира IT и не только
  • Как установить и запустить Python на Windows, Linux и macOS

Декораторы

Python 3 логотип

Декораторы в 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 есть встроенные функции, например sum для суммирования и random для создания случайного числа. Если программисту нужны какие-то свои функции, он может их добавить. Достаточно описать функцию в коде один раз, а затем вызывать её, когда требуется.

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

# функция, которая приветствует пользователя def user_greeting(name): # указываем, что делает функция print("Привет," + name)

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

Что такое декоратор

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

Декоратор можно сравнить с матрёшкой, которая содержит ещё одну матрёшку — другую функцию:

Декораторы в Python: прокачиваем функции

Например, у нас есть функция say_hi() , которая приветствует пользователя. Нам нужно, чтобы в нерабочее время пользователь получал предупреждение, что база недоступна из-за профилактических работ.

В исходном виде функция say_hi() нам в этом не поможет — в ней не хватает нужных команд. Если мы будем добавлять их в исходную функцию, то программа может сломаться — дополнительные функции нужны только тут, а в других местах исходная функция и так работает хорошо.

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

Как сделать декоратор

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

В простейшем виде декоратор выглядит так:

# объявляем функцию, которая будет служить декоратором def my_decorator(func): # объявляем, что декоратор дополняет другую функцию какими-то действиями def wrapper(): # указываем, что должно произойти до вызова другой функции print("Что-то выполняется до функции.") # указываем, что после этого должна работать другая функция func() # указываем, что должно произойти после того, как другая функция отработала print("Что-то выполняется после функции.") return wrapper # объявляем функцию, которую декорируем def say_hi(): # указываем, что делает функция print("Привет!") # указываем, что теперь функция декорирована say_hi = my_decorator(say_hi)

Результат работы этого декоратора будет таким:

Декораторы в Python: прокачиваем функции

Попробуем изменить функцию say_hi() , чтобы она умела ещё и предупреждать о профилактических работах с базой.

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

# импортируем модуль даты и времени from datetime import datetime # объявляем функцию, которая будет служить декоратором def base_maintenance(func): # объявляем, что декоратор дополняет другую функцию какими-то действиями def wrapper(): # проверяем, что время рабочее if 10 
  1. Сначала первая функция проверяет, сколько сейчас времени.
  2. Если время больше 10 утра или меньше 7 вечера, срабатывает обычное приветствие пользователя.
  3. Если время меньше 10 утра или больше 10 вечера, пользователь получает предупреждение, что база недоступна.

При запуске в нерабочее время получаем сообщение:

Декораторы в Python: прокачиваем функции

Получается, мы на основе старой функции получили новые возможности и нам не пришлось её переписывать.

Приведённый выше код получился немного топорным: в нём трижды указано название функции say_hi() . Чтобы сделать код проще, можно использовать символ @:

# вызываем модуль даты и времени from datetime import datetime # объявляем функцию, которая будет служить декоратором def base_maintenance(func): # объявляем, что декоратор дополняет другую функцию какими-то действиями def wrapper(): # проверяем, что время рабочее if 10 

Где применяются декораторы

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

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

Что может пойти не так

Фактически декоратор заменяет другую функцию, и его внутренняя функция wrapper не принимает никаких аргументов. Если бы мы декорировали не функцию say_hi() , а функцию user_greeting(name) , то аргумент name не был бы передан без дополнительных действий.

Ещё один недостаток декораторов: если к другой функции были прикреплены какие-то метаданные, они будут скрыты. Про то, что такое метаданные, — в другой раз.

Что дальше

А дальше мы попробуем на практике поработать с декораторами и сделаем с ними что-то полезное — например, замерим время работы программы или доработаем свою систему логирования.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *