"аутсорсинг" обработки исключений для декоратора

Многие try/except/finally-clauses не только "угадают" мой код, но я часто нахожусь с использованием одинаковой обработки исключений для подобных задач. Поэтому я рассматривал возможность сокращения избыточности путем "аутсорсинга" их на... декоратора.

Поскольку я был уверен, что не буду первым, чтобы прийти к такому выводу, я нашел googled и нашел это - imho - изобретательный recipe что добавило возможность обработки более чем одного исключения.

Но я был удивлен, почему это, похоже, не является широко известной и используемой практикой как таковой, поэтому мне было интересно, есть ли какой-то аспект, который я не рассматривал?

  • Является ли фальшивым использовать шаблон декоратора для обработки исключений или я просто пропустил его все время? Пожалуйста, просветите меня! Каковы подводные камни?

  • Возможно, существует даже пакет/модуль, который поддерживает создание такой обработки исключений разумным образом?

Ответ 1

Самая большая причина для хранения блоков try/except/finally в самом коде состоит в том, что восстановление ошибок обычно является неотъемлемой частью функции.

Например, если у нас есть наша функция int():

def MyInt(text):
    return int(text)

Что делать, если text невозможно преобразовать? Вернуть 0? Вернуть None?

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

Здесь мой подход к простому декоратору:

class ConvertExceptions(object):

    func = None

    def __init__(self, exceptions, replacement=None):
        self.exceptions = exceptions
        self.replacement = replacement

    def __call__(self, *args, **kwargs):
        if self.func is None:
            self.func = args[0]
            return self
        try:
            return self.func(*args, **kwargs)
        except self.exceptions:
            return self.replacement

и использование образца:

@ConvertExceptions(ValueError, 0)
def my_int(value):
    return int(value)

print my_int('34')      # prints 34
print my_int('one')     # prints 0

Ответ 2

В принципе, недостатком является то, что вы больше не решаетесь, как обрабатывать исключение в вызывающем контексте (просто разрешая распространение исключения). В некоторых случаях это может привести к отсутствию разделения ответственности.

Ответ 3

  • Декоратор в Python - это не то же самое, что и шаблон Decorator, если есть некоторое сходство. Не совсем понятно, что вы имеете в виду здесь, но я думаю, что вы имеете в виду один из Python (таким образом, лучше не использовать шаблон слова)

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

  • Вместо декораторов вы можете использовать контекстных менеджеров. И я использую их для этой цели.