Подтвердить что ты не робот

Python - условно улавливающие исключения

Можно ли условно перехватывать исключения в python? Я хотел бы иметь возможность написать функцию, чтобы вызывающий мог решить, кто обрабатывает исключение.

В принципе, мне бы хотелось что-то вроде этого:

def my_func(my_arg, handle_exceptions):
    try:
        do_something(my_arg)
    except Exception, e if handle_exceptions:
        print "my_func is handling the exception"

Я знаю, что могу написать какой-то kludgy-код, который делает то, что я хочу, но я хочу, чтобы питонский ответ был там, где он есть. Спасибо.

4b9b3361

Ответ 1

Вы можете повторно создать исключение, если вы не хотите его обрабатывать:

def my_func(my_arg, handle_exceptions):
    try:
        do_something(my_arg)
    except Exception, e:
        if not handle_exceptions:
            # preserve prior stack trace
            raise

            # Or, if you dont care about the stack prior to this point
            #raise Exception(e)

            # similarly, you can just re-raise e.  The stack trace will start here though.
            #raise e
        else:
            print "my_func is handling the exception"

Другим вариантом является создание собственных исключений, которые относятся к подклассу Exception (или к конкретному исключению, например urllib2.HTTPError), а затем только catch/throw (raise) ваше настраиваемое исключение:

class MyException(Exception):
    def __init__(self, message):
        self.message = message

class MyExceptionTwo(Exception):
    def __init__(self, message):
        self.message = message
    def __repr__(self):
        return "Hi, I'm MyExceptionTwo.  My error message is: %s" % self.message

def something():
    if not tuesday:
        raise MyException("Error: it not Tuesday.")
    else:
        raise MyExceptionTwo("Error: it Tuesday.")

def my_func(my_arg):
    try:
        something()
    except MyException, e:
        print e.message
    # Will pass MyExceptionTwo up the call chain

def my_other_func():
    try:
        my_func(your_arg)
    except MyExceptionTwo, e:
        print str(e)
    # No need to catch MyException here since we know my_func() handles it
    # but we can hadle MyExceptionTwo here

Ответ 2

У вопроса просто недостаточно ответов; -)

Вот еще один для записей. Просто создайте фиктивное исключение:

class NeverMatch(Exception):
    'An exception class that is never raised by any code anywhere'

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

try:
    do_something(my_arg)
except (Exception if handle_exceptions else NeverMatch) as e:
    print 'I am handling it'

Ответ 3

Вы можете использовать:

def my_func(my_arg, handle_exceptions):
  try:
    do_something(my_arg);
  except Exception as e:
    if not handle_exceptions: raise
    print "my_func is handling the exception";

Ответ 4

Тип исключения может быть переменной.

def my_func(my_arg, handle_exceptions):
  if handle_exceptions:
    exc_type = Exception
  else:
    exc_type = None

  try:
    do_something(my_arg);
  except exc_type, e:
    print "my_func is handling the exception";

Obfuscated Python ( "Pythonic"?) версия:

def my_func(my_arg, handle_exceptions):
  try:
    do_something(my_arg);
  except (handle_exceptions and Exception), e:
    print "my_func is handling the exception";

Работает без скобок, на самом деле, но пока мы запутаны, не путайте людей с малоизвестными правилами, такими как приоритет для исключений.

Ответ 5

Вы всегда можете его поймать и условно переподнять так:

def my_func(my_arg, handle_exceptions):
  try:
    do_something(my_arg)
  except Exception:
    if handle_exceptions:
      print "my_func is handling the exception"
      #handle it
    else: 
      print "my_func is NOT handling the exception"
      raise

Ответ 6

Да. Я предпочитаю положительные условия, когда это имеет смысл:

def my_func(my_arg, handle_exceptions):
  try:
    do_something(my_arg);
  except Exception, e:
    if handle_exceptions:
        print "my_func is handling the exception"
    else:
        raise

Ответ 7

У вас есть два основных варианта:

  • Относитесь к handle_exceptions как к булевому, и re-raise if False
  • Лечить handle_exceptions как исключения для обработки

В булевом маршруте у вас есть два основных варианта:

def my_func(my_arg, handle_exceptions):
    try:
        do_something(my_arg)
    except Exception, e:
        if not handle_exceptions:
            raise
        print "my_func is handling the exception"

или

def my_func(my_arg, handle_exceptions):
    if handle_exceptions:
        exceptions = ValueError, IndexError # or whatever
    else:
        exceptions = NoExceptions # None in 2.x, or custom Exception class in 3.x
    try:
        do_something(my_arg)
    except exceptions, e:
        print "my_func is handling the exception"

По пути 'treat handle_exceptions в качестве исключения для обработки маршрута вы можете сделать это:

class NoExceptions(Exception):
    'Dummy exception, never raised'

def my_func(my_arg, handle_exceptions=NoExceptions):
    try:
        do_something(my_arg)
    except handle_exceptions, e:
        print "my_func is handling the exception"

и вы бы назвали его так:

my_func(some_arg, ValueError)  # to handle ValueErrors

или

my_func(some_arg)  # to not handle any exeptions

Это имеет преимущество/недостаток, заключающийся в том, что вызывающий пользователь может указать, какие исключения обрабатываются. Если вы берете этот последний маршрут, вы также можете указать обработчик исключений, возможно, что-то вроде этого:

def my_func(my_arg, handle_exceptions=NoExceptions, handler=None):
    try:
        do_something(my_arg)
    except handle_exceptions, e:
        if handler is not None:
            handler(e)
        else:
            log_this_exception()

Ответ 8

Приятно выглядящая техника для улучшения других ответов заключается в том, чтобы обернуть логику условной обработки исключений в менеджер контекста, который можно повторно использовать, например:

from contextlib import contextmanager 

@contextmanager
def ignore_errors_if(exception_type, skip_condition): 
    try: 
        yield 
    except exception_type, excn: 
        if skip_condition: 
            logging.debug("SKIPPING EXCEPTION %s" % excn)  # etc... 
            pass 
        else: 
            raise excn 

Затем в вашем коде вы можете сказать:

def some_function(): 
    # ... 
    with ignore_errors_if(Exception, should_ignore_errors): 
        result = some_funciton_that_might_raise() 
    # Deal with result, although know that it might not be set...