Comments 10
Кроме декораторов еще очень удобно использовать профайлинг в виде контекстных менеджеров: с ними можно запускать код внутри with выражения. Увидел такое впервые в коде YOLOv8, с тех пор пользуюсь
Ccылка на Github
exception_handler - скорее exception_silencer. Очень плохо использовать такие декораторы и в целом конструкции вида:
try: return func(*args, **kwargs) except Exception as e: print(f"An exception occurred: {str(e)}")
при действительной ошибке внутри функции она будет заглушена, никто о ней не узнает - а workflow пойдёт дальше так, как будто функция вернула None - и вся программа поведёт себя непредсказуемо.
validate_input - похоже на какую-то попытку сделать из нетипизированного языка типизированный. Можно подумать, как будто проверять каждый раз тип аргумента - lambda y: isinstance(y, str) - хорошая идея, но это совсем не так. Нужен типизированный язык - есть полно таких языков, не нужно переделывать Python.
timer - давно уже рекомендуется вместо time.time() использовать time.perf_counter()
Я лично пользовался таким декоратором: соединенные вместе декораторы timer и debug + счетчик отступов:
def performance_debug(f):
performance_debug.active = 0
def tt(self, *args, **kwargs):
turn_on = getattr(self, 'performance_debug', False)
if turn_on:
performance_debug.active += 1
t0 = time.perf_counter()
tabs = '\t'*(performance_debug.active - 1)
name = f.__name__
print('{tabs}Executing <{name}>'.format(tabs=tabs, name=name))
res = f(self, *args, **kwargs)
print('{tabs}Function <{name}> execution time: {time:.3f} ms'.format(
tabs=tabs, name=name, time=(time.perf_counter() - t0)*1000))
performance_debug.active -= 1
return res
else:
return f(self, *args, **kwargs)
return tt
exception_handler - скорее exception_silencer. Очень плохо использовать такие декораторы и в целом конструкции вида
Натыкался на похожий момент в работе (язык другой был, но суть та же) - последствия были катастрофические: валящийся в случайные моменты сервис при кристально чистых логах, а код был от подрядчика, который сдал работы и занялся другими проектами. В итоге, расследование было долгим, нудным и печальным, а главное - дорогим в контексте стоимости простоя сервиса. Люди, пожалуйста, не делайте так
validate_input - похоже на какую-то попытку сделать из нетипизированного языка типизированный.
Это похоже на попытку убедиться что на входе получено ровно то что ожидается - очень хорошая практика.
Как минимум валидация помогает при отладке, как максимум - во всех остальных случаях когда у вас нет 101% уверенности что на входе будут только ожидаемые данные, например, если вы пишите библиотеку, или если значения аргументов получены извне (введены пользователем, переданы API etc).
Гораздо проще аннотировать аргументы и отдать валидацию декоратору чем вручную это делать внутри каждой функции где существует риск, не говоря уже про DRY.
Дополнительная польза валидации на входе - это чёткое сообщение о том что и где пошло не так, а не непонятное исключение где-то в глубине которое возникло по причине неверного аргумента.
Впрочем, расчитывать на то что неправильный аргумент обязательно вызовет исключение не стоит - к примеру, если у вас на входе ожидается целое число в диапазоне от 3 до 7 и оно используется в вычислениях, но при этом на вход кто-то подал 13 - исключения не будет, будет неверный результат.
В конце концов, pydantic
и его @validate_arguments
не просто так появились - тот кто игнорирует валидацию обязательно нарвётся, или хуже того - подставит кого-то ещё.
А зачем для вывода debug - сообщений использовать print, не лучше использовать библиотеку logging?
exception_handler - скорее exception_silencer. Очень плохо использовать такие декораторы и в целом конструкции вида
Тут приведен пример, дальше никто не мешает его доработать так, как требуется для приложения. У меня в коде внутри декоратора отлавливается сразу много типов ошибок и есть логика, соответствующая каждому типу. Все логируется. Но при этом я точно знаю, что наружу прилетит не некий непонятный краш, а осмысленное сообщение об ошибке, которое дальше отправит пользователя продукта ко мне сообщить, что там что-то пошло не так. Это если я сам не успею через систему мониторинга увидеть эту ошибку, потому что да, декоратор еще и выхлоп в туда даст, а она уже заорет, что что-то пошло не так. Декоратор же избавляет меня от необходимости ваять цепочки try...except. Более того, в коде в самых неожиданных местах я спокойно сам рэйзю исключение, которое в декораторе превращается в осмысленный респонс наружу. Мне не надо думать, как из места возникновения ожидаемой ошибки сформировать и выкинуть наружу правильный респонс. Он сам сформируется в декораторе и улетит потребителю.
validate_input - похоже на какую-то попытку сделать из нетипизированного языка типизированный.
Опять же, это про другое. Автором показана идея. Типизированность тут вообще не при чем. Ни один типизированный язык не наложит ограничений на значения. Например, в функцию передается возраст. Наверное логично, что возраст в 100500 лет явно будет ошибкой. Какая тут типизация, тут явная валидация входных данных. Так понимаю, что у автора validations - это словарь методов для валидации значений, но не проверки их типов. И это да, вполне удобное решение.
Для валидации я сделал вот такой декоратор:
https://github.com/EvgeniyBurdin/valdec
А для исключений (ну и заодно, для обработки "до" и "после"), вот такой:
Вместо retry - tenacity.
Если вам нужен retry - рекомендую библиотеку tenacity https://pypi.org/project/tenacity/
Она предлагает retry декоратор с разными политиками.
Ааа-ааа! Уберите от экранов джунов, они потом такое начнут в реальный код вставлять!
Пять декораторов Python, которые могут сократить код в два раза