11

为了弄清楚如何避免某些递归,我需要捕获任何异常(编辑:不仅是从 Exception 派生的异常,还有所有异常,包括 KeyboardInterrupt 和用户异常),将其放入变量中,然后重新引发它在 catch 块之外。本质上,我正在尝试推出自己的 finally 块。这可能吗?

实际的问题是调用许多清理函数,如果其中任何一个失败,则还应调用所有其他函数,那么失败的异常仍应传播。这是我当前的解决方案,它需要一个 Popen 对象列表:

def cleanupProcs(procs):
    if not procs:
        return

    proc = procs.pop(0)
    try:
        proc.terminate()
        proc.wait()
    finally:
        cleanupProcs(procs)

有没有迭代的方法来做到这一点?更优雅的方式?一种更 Pythonic 的方式?

4

7 回答 7

12

如果您希望包含堆栈跟踪:

try:
    # something
except:
    the_type, the_value, the_traceback = sys.exc_info()

之后

raise the_type, the_value, the_traceback

(与此答案有关)

另请参阅此处了解 Python 2.7。

于 2013-10-16T14:29:09.680 回答
4

在这种情况下,我想我会争辩说你可能做得不对。

对我来说,例外的意义在于表明一个bum bum baaaaa 例外情况。当您编写可能引发异常的代码时,您有两个负责任的选择 - 捕获它并对其进行处理(如恢复),或完全忽略它。

从您在帖子中所说的内容来看,您并不真正关心发生了异常。它不应该停止您的程序,并且程序流程应该照常继续。但是,您确实想知道发生了异常。这就是日志模块的用武之地:

import logging
log = logging

def get_some_cheese():
    raise ValueError("Sorry, we're right out.")

try:
    get_some_cheese()
except:
    log.exception("What a waste of life")

当您记录异常时,它会自动为您添加堆栈跟踪信息。稍微配置一下日志记录后,您可以将其设置为执行各种您想做的事情 - 发送电子邮件、写入文件或 stdout/err,等等。但是随后您会被告知发生了异常,但您也可以简单地从错误中恢复并继续以愉快的方式清理您需要清理的任何内容。

于 2013-10-16T14:40:51.850 回答
2

我可能会BaseException用来捕获任何被抛出的东西,并遍历所有清理函数(而不是使用递归)。然后将任何异常附加到列表中,以便在完成清理时酌情处理(重新引发、记录等)。

def runCleanup(procs):
    exceptions = []
    for proc in procs:
        try:
            proc.terminate()
            proc.wait()
        except BaseException as e:
            exceptions.append(e) # Use sys.exc_info() for more detail

    return exceptions # To be handled or re-raised as needed
于 2013-10-16T18:05:53.623 回答
1

您可以使用:

procexceptions = []

except Exception, e:
    procexceptions.append(e)

然后稍后(在终止进程的循环之后)你可以

raise procexceptions[0]

等等

于 2013-10-16T14:33:50.010 回答
0

Openstack 出于不同的原因做了一些非常相似的事情。看看https://github.com/openstack/nova/blob/master/nova/openstack/common/excutils.py#L30(函数save_and_reraise_exception)。在他们的情况下,它就像资源管理器一样工作。

于 2013-10-16T14:27:02.183 回答
0

就像 Python 中的几乎所有其他东西一样,异常是对象,因此可以绑定到名称并对其进行操作。这是一个简短的示例,展示了如何获取异常并在以后使用它:

>>> def to_int(x):
...     try:
...         return int(x)
...     except Exception, e:
...         print 'in exception block:', e
...     print 'after exception block:', e

>>> to_int('12')
12
>>> to_int('abc')
in exception block: invalid literal for int() with base 10: 'abc'
after exception block: invalid literal for int() with base 10: 'abc'
于 2013-10-16T14:29:45.640 回答
0

这很容易:

>>> try:
...     #something
... except BaseException, e: # OK. Using BaseException instead of Exception
...     pass
... 
>>> 
>>> raise e
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
>>> 
于 2013-10-16T14:37:54.953 回答