Python декораторы.
Как-то долго я ходил вокруг да около, а про любимый Python забыл. Сегодня попытаюсь объяснить вам что такое декораторы.
Декоратор - наверное, самый популярный паттерн, суть которого заключается в том, чтобы максимально быстро и просто расширить функционал исходной функции. Также стоит запомнить такое правило: декоратор - функция, которая в качестве аргумента принимает функцию и возвращает функцию. Давайте сразу к примеру:ᅠ
1. Определим функцию say_hello(name), которая на вход получит имя человека:
#python #github
Как-то долго я ходил вокруг да около, а про любимый Python забыл. Сегодня попытаюсь объяснить вам что такое декораторы.
Декоратор - наверное, самый популярный паттерн, суть которого заключается в том, чтобы максимально быстро и просто расширить функционал исходной функции. Также стоит запомнить такое правило: декоратор - функция, которая в качестве аргумента принимает функцию и возвращает функцию. Давайте сразу к примеру:ᅠ
1. Определим функцию say_hello(name), которая на вход получит имя человека:
python2. Отлично. А теперь определим декоратор, который скажет что-то приятное после приветствия:
def say_hello(name):
print('Hello,', name)
say_hello('Денис')
>>> Hello, Денис
def you_are_beautiful(func): # На вход поступает функцияЕсть один нюанс, который нужно соблюсти - декоратор должен быть объявлен ДО декорируемой функции. А способов применить декоратор несколько, но для Python самый актуальный способ, так называемый «Синтаксический сахар», на примере:
def wrapped(*args, **kwargs): # обрабатываем аргументы
func(*args, **kwargs) # вызывает функцию извне
print('Классно выглядишь сегодня ;)')
return wrapped # возвращает функцию, но не вызывает!!!
# (возвращает ссылку на функцию, без скобок)
# сначала определяем декораторПод коробкой вызов функции выглядит так:
def you_are_beautiful(func):
def wrapped(*args, **kwargs):
func(*args, **kwargs)
print('Классно выглядишь сегодня ;)')
return wrapped
# далее определяем функцию и навешиваем декоратор
@you_are_beautiful <-- наш декоратор
def say_hello(name):
print('Hello,', name)
say_hello('Денис')
>>> Hello, Денис
>>> Классно выглядишь сегодня ;)
you_are_beautiful(say_hello('Денис'))Ну и, соответственно, декораторов может быть навешано несколько:
@decorator1По итогу получаем задекорированную функцию. Что-то выполнилось до вызова функции, что-то после, и это очень удобно в некоторых задачах. Обещаю сделать отдельный репозиторий/папку для своих декораторов, прикреплю ссылку сюда позже, а отдельным постом оповещу. И спасибо за прочтение, это важно дня меня 🙃
@decorator2
@decorator3
@decorator4
def foo():
pass
# что при вызове, по сути, эквивалентно этому:
decorator1(decorator2(decorator3(decorator4(foo()))))
#python #github
Сегодня будет достаточно глубокий экскурс в Python исключения.ᅠ
Для кого-то эта тема может быть очевидной, но для некоторых начинающих разработчиков эта тема всё ещё не открыта. Итак, что же такое исключения?
Действия программы, которые противоречат тем или иным правилам вызывают исключения. Например попытка поделить на ноль вызовет
"Отловить" ошибки можно при помощи конструкции
Как же создать своё исключение? Легко:
1. Большое количество исключений - всегда сложно, запутывает и разработчиков, и пользователей.
2. Лучше предусмотреть альтернативный выход из исключительной ситуации.
3. Плохо влияет на будущую поддерживаемость и ясность вашего кода.
4. Все исключения всё равно не получится поймать.
Я уже рассказывал вам про декораторы тут, так что вот какое решение могу предложить. Оформим собственное исключение, декоратор, что сохранит любую нашу функцию и задекорируем объявленную ранее функцию
Ну и естественно моё решение далеко не эталонно, но оно уже в разы лучше и удобнее, чем писать в каждой функции свой собственный обработчик (за редким исключением). Если вам нужно быстро обезопасить свою функцию, то это вполне себе неплохое решение.
Способов решить проблему исключений есть ещё много, а весь материал, что я изложил тут, чрезвычайно сжат. Новичкам я конечно же советую погрузиться в тему глубже. Это важнее, чем кажется.
А этот декоратор был бы неплохим началом для моей коллекции декораторов, да?)
#python
Для кого-то эта тема может быть очевидной, но для некоторых начинающих разработчиков эта тема всё ещё не открыта. Итак, что же такое исключения?
Действия программы, которые противоречат тем или иным правилам вызывают исключения. Например попытка поделить на ноль вызовет
ZeroDivisionError
, а попытка сложить строку и кортеж - TypeError
."Отловить" ошибки можно при помощи конструкции
try/except/else/finally
. Например:def division(a,b):Теперь работа программы не будет ломаться при делении на ноль, а продолжит свою работу. Но это далеко не значит, что использование блока
try:
return a/b
except ZeroDivisionError:
return None
try/except
- панацея. Об этом чуть позже. Как же создать своё исключение? Легко:
class MyOwnException(Exception):Как вы видите, для создания своего исключения мы создаём новый класс, который наследуем от базового класса
pass
или
class MyOwnException(Exception):
# разный функционал, что выполнится при исключении
Exception
. Чтобы выбросить это исключение в программе мы воспользуемся оператором raise
:# какое-то условие, что вызывает исключениеЗамечательно, удобно для программиста, быстро но теперь поговорим о том почему это плохо:
raise MyOwnException('Message')
>>> __main__.MyOwnException: Message
1. Большое количество исключений - всегда сложно, запутывает и разработчиков, и пользователей.
2. Лучше предусмотреть альтернативный выход из исключительной ситуации.
3. Плохо влияет на будущую поддерживаемость и ясность вашего кода.
4. Все исключения всё равно не получится поймать.
Я уже рассказывал вам про декораторы тут, так что вот какое решение могу предложить. Оформим собственное исключение, декоратор, что сохранит любую нашу функцию и задекорируем объявленную ранее функцию
division
. Вашему вниманию предлагаю такой код:from functools import wrapsИтак, что тут происходит? Для начала о
class Error(Exception):
def __repr__(self):
return 'Error'
def __str__(self):
return 'Error'
def safe(func):
@wraps(func)
def wrapped(*args, **kwargs):
try:
return func(*args, **kwargs)
except:
return Error()
return wrapped
@safe # декорируем функцию
def division(a, b):
return a / b
division(4,0)
>>> Error
@wraps
. Я еще не рассказывал о нём, но этот декоратор нужен для сохранения таких параметров функции как __name__
и __doc__
, так как после декорирования эти параметры переписываются. Под коробкой этот декоратор на примере можно заменить вот так вот так:def safe(func)А теперь обо всём в целом. Мы создали декоратор
def wrapped(*args, **kwargs):
try:
return func(*args, **kwargs)
except:
return Error()
wrapped.__name__ = func.__name__
wrapped.__doc__ = func.__doc__
return wrapped
safe
, который позволит обезопасить любую функцию от любой ошибки. При объявлении небезопасной функции с этим декоратором мы получаем абсолютно неломаемую программу, а выглядит решение ну очень уж лаконично. Кстати, в блоке except мы можем возвращать не только наш экземпляр класса Error
, но и любое другое значение, которое нам будет удобно, например float('inf')
, что вернёт нам бесконечность. (Кстати, в рассмотренном примере не обязательно возвращать именно экземпляр класса. С таким же успехом можно вернуть просто строку "Error"
, но я оставил класс для того, чтобы вы могли легко модифицировать поведение своей ошибки)Ну и естественно моё решение далеко не эталонно, но оно уже в разы лучше и удобнее, чем писать в каждой функции свой собственный обработчик (за редким исключением). Если вам нужно быстро обезопасить свою функцию, то это вполне себе неплохое решение.
Способов решить проблему исключений есть ещё много, а весь материал, что я изложил тут, чрезвычайно сжат. Новичкам я конечно же советую погрузиться в тему глубже. Это важнее, чем кажется.
А этот декоратор был бы неплохим началом для моей коллекции декораторов, да?)
#python