Pull to refresh

Comments 10

Кроме декораторов еще очень удобно использовать профайлинг в виде контекстных менеджеров: с ними можно запускать код внутри with выражения. Увидел такое впервые в коде YOLOv8, с тех пор пользуюсь

Ccылка на Github

  1. exception_handler  - скорее exception_silencer. Очень плохо использовать такие декораторы и в целом конструкции вида:

    try:             
      return func(*args, **kwargs)
    except Exception as e:
      print(f"An exception occurred: {str(e)}")

при действительной ошибке внутри функции она будет заглушена, никто о ней не узнает - а workflow пойдёт дальше так, как будто функция вернула None - и вся программа поведёт себя непредсказуемо.

  1. validate_input - похоже на какую-то попытку сделать из нетипизированного языка типизированный. Можно подумать, как будто проверять каждый раз тип аргумента - lambda y: isinstance(y, str) - хорошая идея, но это совсем не так. Нужен типизированный язык - есть полно таких языков, не нужно переделывать Python.

  2. 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 - это словарь методов для валидации значений, но не проверки их типов. И это да, вполне удобное решение.

Ааа-ааа! Уберите от экранов джунов, они потом такое начнут в реальный код вставлять!

Sign up to leave a comment.