22

我连续有很多行可能会引发异常,但无论如何,它仍然应该继续下一行。如何在不单独尝试捕获可能引发异常的每个语句的情况下做到这一点?

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

我认为只有当异常很严重时代码才应该在异常之后继续运行(计算机会烧毁或整个系统会搞砸,它应该停止整个程序,但是对于许多小事情也会抛出异常,例如连接失败等)我通常在异常处理方面没有任何问题,但在这种情况下,我使用的是第 3 方库,它很容易为小事情引发异常。

在查看 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
4

6 回答 6

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

这里有两点需要注意:

  • 您要执行的所有操作都必须由具有相同签名的可调用对象表示(在示例中,可调用对象不带参数)。如果它们还没有,请将它们包装在小函数、lambda表达式、可调用类等中。
  • except子句是个坏主意,但您可能已经知道这一点。

另一种更灵活的替代方法是使用高阶函数,例如

def logging_exceptions(f, *args, **kwargs):
    try:
        f(*args, **kwargs)
    except Exception as e:
        print("Houston, we have a problem: {0}".format(e))
于 2012-06-05T14:09:51.947 回答
3

我遇到了类似的事情,并在这里问了一个关于 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与抑制器一起使用以运行所有命令。

于 2012-06-05T14:16:39.367 回答
0

您可以使用装饰器处理这样的任务:

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
于 2012-06-05T14:34:14.607 回答
0

有时,当语言无法支持您表达想法的优雅方式时,因为语言开发在过去几十年中确实失败了,您只能依靠 Python 仍然是支持 exec 语句的动态语言这一事实​​,这使得以下情况成为可能:

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

这个新的操作符使代码更加pythonic和优雅,因为您不需要指定额外的 if 语句来保证索引保持绑定或函数确实成功,这与我们想要表达的内容完全无关(它只是不应该stop) 在这里(注意:虽然可以通过基于列表类创建一个类来实现安全索引,但只要有 try catch 就可以使用此运算符),在 Lisp 中,以 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 使用 preparse-function 在代码到达 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
于 2013-11-09T02:35:23.793 回答
0

除了提供的答案之外,我认为值得一提的是,try-except已经提出了单行声明 - 请参阅相关的PEP 463以及不幸的拒绝通知:

""" I want to reject this PEP. I think the proposed syntax is acceptable given the
desired semantics, although it's still a bit jarring. It's probably no worse than the
colon used with lambda (which echoes the colon used in a def just like the colon here
echoes the one in a try/except) and definitely better than the alternatives listed.

But the thing I can't get behind are the motivation and rationale. I don't think that
e.g. dict.get() would be unnecessary once we have except expressions, and I disagree 
with the position that EAFP is better than LBYL, or "generally recommended" by Python. 
(Where do you get that? From the same sources that are so obsessed with DRY they'd rather
introduce a higher-order-function than repeat one line of code? :-)

This is probably the most you can get out of me as far as a pronouncement. Given that
the language summit is coming up I'd be happy to dive deeper in my reasons for rejecting
it there (if there's demand).

I do think that (apart from never explaining those dreadful acronyms :-) this was a 
well-written and well-researched PEP, and I think you've done a great job moderating the 
discussion, collecting objections, reviewing alternatives, and everything else that is 
required to turn a heated debate into a PEP. Well done Chris (and everyone who 
helped), and good luck with your next PEP! """
于 2018-10-09T14:24:39.067 回答
-2
try:
    this_may_cause_an_exception()
except:
    logging.exception('An error occured')
finally:
    but_I_still_wanna_run_this()
    and_this()
    and_also_this()

您可以使用finally异常处理块。它实际上是为了清理代码。

编辑:我看到你说所有函数都可以抛出异常,在这种情况下,larsmans 的答案是我能想到的最干净的答案,可以为每个函数调用捕获异常。

于 2012-06-05T14:12:36.923 回答