[Python] Декораторы Python: хватит это терпеть
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Конец страданиям.Всем привет! В этой статье я расскажу об инструменте, разработанном мной, который изменяет работу декораторов в Python и делает их более «Питоничными».Я не буду рассказывать про области применения декораторов. Есть множество статей на эту тему.Для начала, давайте вспомним: что же такое декораторы в Пайтон.Если совсем просто, то это удобный способ передать одну функцию в другую и получить третью. В этом определении нет ни одного слова правды, но мы вернёмся к этому позже.Давайте разбираться!Как работают декораторы
def decorator_function(wrapped_func):
def wrapper():
print('Входим в функцию-обёртку')
print('Оборачиваемая функция: ', wrapped_func)
print('Выполняем обёрнутую функцию...')
wrapped_func()
print('Выходим из обёртки')
return wrapper
Так выглядит функция-декоратор. Как вы можете увидеть, она принимает в качестве аргумента другую функцию. Затем с этой функцией что-то делают внутри вложенной функции-обёртки и возвращают из декоратора уже обёртку вместо исходной функции.Теперь можно декорировать:
@decorator_function
def hello_world():
print('Hello world!')
hello_world()
Здесь декоратор получает функцию hello_world, и подменяет её своей вложенной функцией wrapper.Вывод:
Входим в функцию-обёртку
Оборачиваемая функция: <function hello_world at 0x0201B2F8>
Выполняем обёрнутую функцию...
Hello world!
Выходим из обёртки
Важно помнить!Декоратор исполняется только один раз: при объявлении оборачиваемой функции. При дальнейшем вызове функции исполняется только вложенная функция wrapper.Мы это увидим, если добавим две строчки в наш декоратор:
def decorator_function(wrapped_func):
print('Входим в декоратор')
def wrapper():
...
print('Выходим из декоратора')
return wrapper
@decorator_function
def hello_world():
print('Hello world!')
Входим в декоратор
Выходим из декоратора
hello_world()
Входим в функцию-обёртку
Оборачиваемая функция: <function hello_world at 0x0201B2F8>
Выполняем обёрнутую функцию...
Hello world!
Выходим из обёртки
А вот и страдания: аргументы функции и аргументы декоратораУ функции, которую мы декорируем, могут быть аргументы. Принимает их вложенная функция wrapper:
def decorator_function(wrapped_func):
def wrapper(*args):
...
wrapped_func(args)
...
return wrapper
@decorator_function
def hello_world(text):
print(text)
hello_world('Hello world!')
А ещё, аргументы могут быть переданы непосредственно в декоратор:
def fictive(decorator_text):
def decorator_function(wrapped_func):
def wrapper(*args):
print(decorator_text, end='')
wrapped_func(*args)
return wrapper
return decorator_function
@fictive(decorator_text='Hello, ')
def hello_world(text):
print(text)
hello_world('world!')
Здесь аргумент decorator_text передаётся при декорировании в строке №11 и попадает в функцию fictive, строка №1. Таким образом, появился ещё один уровень вложенности только для того, чтобы принять аргументы декоратора.Вывод:
Hello, world!
Пойдём дальше. А что, если декоратор может быть, в одних случаях с аргументами, в других - без аргументов? Поехали!
def fictive(_func=None, *, decorator_text=''):
def decorator_function(wrapped_func):
def wrapper(*args):
print(decorator_text, end='')
wrapped_func(*args)
return wrapper
if _func is None:
return decorator_function
else:
return decorator_function(_func)
@fictive
def hello_world(text):
print(text)
hello_world('Hello, world!')
@fictive(decorator_text='Hi, ')
def say(text):
print(text)
say('world!')
Вывод:
Hello, world!
Hi, world!
Как Вам код? Вспомним, мантру Питонистов из начала статьи:
Декораторы - это удобный способ передать...
Ничего, на помощь придёт DecoratorHelper! Но, перед этим, ещё пара слов о декораторах.Мифы декораторов
- Декораторы удобны. Думаю, с этим мы уже разобрались.
- В декораторы нужно передавать функции. Передавать можно не только функции, но и любые callable объекты. Это такие объекты, у которых определён дандер метод (магический метод) __call__. Этот метод отвечает за операции, которые будут произведены при вызове объекта (когда вы ставите скобочки после имени объекта: object()). Вместо функции может быть метод или класс.
- Декораторы - это функции. И опять: это может быть любой callable объект.
- Декоратор возвращает функцию. Декоратор может возвращать что угодно. Стоит лишь помнить, что если декоратор возвращает не callable объект, то вызывать его не получится.
- Передавать можно не только функции, но и аргументы. Вместо функции может быть метод или некоторые классы.
DecoratorHelper: решение проблемУстанавливаем модуль:
pip install DecoratorHelper
Импортируем и используем как декоратор:
from DecoratorHelper import DecoratorHelper
@DecoratorHelper
def hello_world(text):
print(text)
hello_world('Hello, world!')
Что это даёт?
- Вы больше не думаете над тем, будут ли аргументы у декоратора. DecoratorHelper думает об этом вместо Вас.
- Вы получаете удобный, Питоничный доступ ко всем аргументам, самой функции, к тому, что будет происходить до и после выполнения функции.
В итоге Вы получаете вместо функции объект, который имеет следующие атрибуты:
- self.function - оборачиваемая функция
- self.decorator_args - аргументы декоратора. Кортеж позиционных аргументов, последний элемент которого - словарь с именованными аргументами.
- self.function_args - аргументы функции. Кортеж позиционных аргументов, последний элемент которого - словарь с именованными аргументами.
- self.pre_function - то, что будет происходить перед выполнением функции (так можно превратить функцию в коллбэк).
- self.post_function - то, что будет происходить после выполнения функции (так можно добавить функции в коллбэк).
Как использовать?Перепишем приведённый ранее код декоратора, который может принимать/не принимать аргументы:
from DecoratorHelper import DecoratorHelper
def fictive(object):
object.pre_function = lambda : print(*object.decorator_args[:-1], end='')
return object
@fictive
@DecoratorHelper('Hello, ')
def hello_world(text):
print(text)
hello_world('world!')
Как мы можем видеть, тело декоратора сократилось в 8 раз. Profit!Ограничение! Первым аргументом нельзя передавать callable объекты, иначе всё сломается :) Думаю, для большинства задач, это не смертельно...Что дальше?В следующих версиях планируется:
- Улучшенная обработка аргументов.
- Встроенный счётчик вызовов.
- Возможность превратить объект в синглтон.
- Возможность превратить объект в буилдер.
- Может быть, возможность подключить асинхронность.И всё это в максимально удобном формате: singleton = True.
P. S. Если в комментариях будет интерес к теме, напишу вторую статью о том, как DecoratorHelper устроен. Но сразу скажу, что это уровень Junior+.
===========
Источник:
habr.com
===========
Похожие новости:
- [Python, Разработка мобильных приложений, Лайфхаки для гиков] Как учить протоколы без чтения RFC: как сэкономить время при разработке
- [Математика, Машинное обучение, Управление проектами, Управление продуктом] Учиться, учиться, и ещё раз учиться?
- [Python] Генераторы для самых маленьких
- [Python, Django] Конечные автоматы и django
- [Python, Алгоритмы, Big Data, Финансы в IT] Инструменты для алготрейдинга на Python. SMA + Полосы Боллинджера на акциях Северстали + код готовой стратегии
- [Разработка веб-сайтов, Python] Первые шаги в aiohttp
- [Python, Алгоритмы, Искусственный интеллект] Анализ результатов работы архитектуры YoloV3 на медицинских снимках (перевод)
- [Информационная безопасность, Python, Программирование, Алгоритмы] Реализация алгоритмов хеширования семейства SHA-2
- [Python] Сокрытые драгоценности Python (перевод)
- [Ruby, Python, Программирование, Будущее здесь] Почему за интерпретируемыми языками будущее
Теги для поиска: #_python, #_decoratorhelper, #_dekoratory (Декораторы), #_python, #_piton (Питон), #_pajton (Пайтон), #_decorators, #_python
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 01:39
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Конец страданиям.Всем привет! В этой статье я расскажу об инструменте, разработанном мной, который изменяет работу декораторов в Python и делает их более «Питоничными».Я не буду рассказывать про области применения декораторов. Есть множество статей на эту тему.Для начала, давайте вспомним: что же такое декораторы в Пайтон.Если совсем просто, то это удобный способ передать одну функцию в другую и получить третью. В этом определении нет ни одного слова правды, но мы вернёмся к этому позже.Давайте разбираться!Как работают декораторы def decorator_function(wrapped_func):
def wrapper(): print('Входим в функцию-обёртку') print('Оборачиваемая функция: ', wrapped_func) print('Выполняем обёрнутую функцию...') wrapped_func() print('Выходим из обёртки') return wrapper @decorator_function
def hello_world(): print('Hello world!') hello_world() Входим в функцию-обёртку
Оборачиваемая функция: <function hello_world at 0x0201B2F8> Выполняем обёрнутую функцию... Hello world! Выходим из обёртки def decorator_function(wrapped_func):
print('Входим в декоратор') def wrapper(): ... print('Выходим из декоратора') return wrapper @decorator_function
def hello_world(): print('Hello world!') Входим в декоратор
Выходим из декоратора hello_world()
Входим в функцию-обёртку
Оборачиваемая функция: <function hello_world at 0x0201B2F8> Выполняем обёрнутую функцию... Hello world! Выходим из обёртки def decorator_function(wrapped_func):
def wrapper(*args): ... wrapped_func(args) ... return wrapper @decorator_function def hello_world(text): print(text) hello_world('Hello world!') def fictive(decorator_text):
def decorator_function(wrapped_func): def wrapper(*args): print(decorator_text, end='') wrapped_func(*args) return wrapper return decorator_function @fictive(decorator_text='Hello, ') def hello_world(text): print(text) hello_world('world!') Hello, world!
def fictive(_func=None, *, decorator_text=''):
def decorator_function(wrapped_func): def wrapper(*args): print(decorator_text, end='') wrapped_func(*args) return wrapper if _func is None: return decorator_function else: return decorator_function(_func) @fictive def hello_world(text): print(text) hello_world('Hello, world!') @fictive(decorator_text='Hi, ') def say(text): print(text) say('world!') Hello, world!
Hi, world! Декораторы - это удобный способ передать...
pip install DecoratorHelper
from DecoratorHelper import DecoratorHelper
@DecoratorHelper def hello_world(text): print(text) hello_world('Hello, world!')
from DecoratorHelper import DecoratorHelper
def fictive(object): object.pre_function = lambda : print(*object.decorator_args[:-1], end='') return object @fictive @DecoratorHelper('Hello, ') def hello_world(text): print(text) hello_world('world!')
=========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 01:39
Часовой пояс: UTC + 5