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

Как предотвратить попытку поймать каждую возможную строку в python?

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

try:
    this_may_cause_an_exception()
    but_I_still_wanna_run_this()
    and_this()
    and_also_this()
except Exception, e:
    logging.exception('An error maybe occured in one of first occuring functions causing the others not to be executed. Locals: {locals}'.format(locals=locals()))

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

Я не хочу этого делать:

try:
    this_may_cause_an_exception()
except:
    pass
try:
    but_I_still_wanna_run_this()
except:
    pass
try:
    and_this()
except:
    pass
try:
    and_also_this()
except:
    pass

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

Посмотрев на ответ m4spy, я подумал, что это невозможно, чтобы иметь декоратор, который позволит каждой строке функции выполнять, даже если один из них вызывает исключение.

Что-то вроде этого было бы круто:

def silent_log_exceptions(func):
    @wraps(func)
    def _wrapper(*args, **kwargs):
        try:
            func(*args, **kwargs)
        except Exception:
            logging.exception('...')
            some_special_python_keyword # which causes it to continue executing the next line
    return _wrapper

Или что-то вроде этого:

def silent_log_exceptions(func):
    @wraps(func)
    def _wrapper(*args, **kwargs):
        for line in func(*args, **kwargs):
            try:
                exec line
            except Exception:
                logging.exception('...')
    return _wrapper



@silent_log_exceptions
def save_tweets():
    a = requests.get('http://twitter.com)
    x = parse(a)
    bla = x * x
4b9b3361

Ответ 1

for func in [this_may_cause_an_exception,
             but_I_still_wanna_run_this,
             and_this,
             and_also_this]:
    try:
        func()
    except:
        pass

Здесь можно заметить две вещи:

  • Все действия, которые вы хотите выполнить, должны быть представлены вызывающими вызовами с одной и той же сигнатурой (в примере - вызывающие, которые не принимают аргументов). Если их еще нет, оберните их небольшими функциями, выражениями lambda, вызываемыми классами и т.д.
  • Предложения Bare except - плохая идея, но вы, вероятно, уже знали это.

Альтернативный подход, более гибкий, заключается в использовании функции более высокого порядка, например

def logging_exceptions(f, *args, **kwargs):
    try:
        f(*args, **kwargs)
    except Exception as e:
        print("Houston, we have a problem: {0}".format(e))

Ответ 2

Я столкнулся с чем-то похожим и задал вопрос о SO здесь. Принимаемый ответ обрабатывает ведение журнала и наблюдает только за конкретным исключением. Я закончил с измененной версией:

class Suppressor:
    def __init__(self, exception_type, l=None):
        self._exception_type = exception_type
        self.logger = logging.getLogger('Suppressor')
        if l:
            self.l = l
        else:
            self.l = {}
    def __call__(self, expression):
        try:
            exec expression in self.l
        except self._exception_type as e:
            self.logger.debug('Suppressor: suppressed exception %s with content \'%s\'' % (type(self._exception_type), e))

Используется так:

s = Suppressor(yourError, locals()) 
s(cmdString)

Итак, вы можете настроить список команд и использовать map, чтобы супрессор выполнил все их действия.

Ответ 3

try:
    this_may_cause_an_exception()
except:
    logging.exception('An error occured')
finally:
    but_I_still_wanna_run_this()
    and_this()
    and_also_this()

Вы можете использовать наконец блок обработки исключений. Это действительно предназначено для кода очистки, хотя.

EDIT: Я вижу, что вы сказали, что все функции могут генерировать исключения, и в этом случае ответ larsmans о самом чистом я могу думать о том, чтобы поймать исключение для каждого вызова функции.

Ответ 4

Вы можете справиться с такой задачей с помощью декоратора:

import logging
from functools import wraps

def log_ex(func):
    @wraps(func)
    def _wrapper(*args, **kwargs):
        try:
            func(*args, **kwargs)
        except Exception:
            logging.exception('...')
    return _wrapper

@log_ex
def this_may_cause_an_exception():
    print 'this_may_cause_an_exception'
    raise RuntimeError()

@log_ex
def but_i_wanna_run_this():
    print 'but_i_wanna_run_this'

def test():
    this_may_cause_an_exception()
    but_i_wanna_run_this()

Вызов функции тестирования будет выглядеть (что покажет, что обе функции выполнены):

>>> test()
this_may_cause_an_exception
ERROR:root:...
Traceback (most recent call last):
  File "<stdin>", line 5, in _wrapper
  File "<stdin>", line 4, in my_func
RuntimeError
but_i_wanna_run_this

Ответ 5

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

code="""
for i in range(Square_Size):
    Square[i,i] @= 1
    Square[i+1,i] @= 2
    @dowhatever()
"""

Этот новый оператор делает код более pythonic и изящным, поскольку вам не нужно указывать дополнительные if-statemens, которые гарантируют, что индекс остается в привязке или функция действительно преуспевает, что совершенно не имеет отношения к тому, что мы хотим выразить (это просто не следует останавливаться здесь) (обратите внимание: хотя безопасная индексация будет возможна путем создания класса на основе класса списка, этот оператор работает всякий раз, когда должен быть try catch), в Lisp было бы легко определить его в Лизкий способ, но это швы, которые невозможно определить в элегантном стиле на Python, но все же, вот маленький подготовитель, который сделает это возможным: exec "\n".join([o+"try: "+z.replace("@","")+"\n"+o+"except: pass" if "@" in z else z for z in code.split("\n") for o in ["".join([h for h in z if h==" "])]]) #new <- hackish operator which wraps try catch into line

Результат, предполагая, что Square был 4x4 и содержал только нули:

[1 0 0 0]
[2 1 0 0]
[0 2 1 0]
[0 0 2 1]

Релевантно: Sage/Sagemath CAS использует подготовительную функцию который преобразует код до того, как он достигнет интерпретатора Python. Патчем для этой функции будет:

def new_preparse(code,*args, **kwargs):
    code="\n".join([o+"try: "+z.replace("@","")+"\n"+o+"except: pass" if "@" in z else z for z in code.split("\n") for o in ["".join([h for h in z if h==" "])]])
    return preparse(code)
sage.misc.preparser.preparse=new_preparse