在Python 3中提升和捕获表达式中的异常

时间:2012-06-18 19:37:14

标签: python exception-handling python-3.x

首先我要说:这是用于深奥的目的 - 而不是生产代码。我正在玩一行Python代码中的东西,因此我需要表达式而不是语句。 (编辑:我正致力于将代码机械编译为单行(大部分)等效的Python代码BitBucket - onelinepython。请注意,它正在进行中,因此我的工作正在进行中最初提到它的沉默)

我基本上想做两件事:

  • 调用一个函数来引发我选择的异常实例:

    raise_exception(WhateverException())

  • 在一个封闭的环境中运行一个函数,在这个环境中我可以获取引发的异常实例(如果有的话),以及被调用的函数的返回值。 E.g:

    has_exception,return_or_exception = check_exception(f,param1,param2,...)

理想情况下,我想用一些默认库或内置函数来做这件事(不管我有多大的压力都要用它的预期用途)。我不需要具有与我提供的示例完全相同的签名的函数,只是我可以将其变成足够接近的东西。但我确实有一个限制:不使用eval()或等效函数

编辑:我知道我可以定义自己的函数来执行此操作,但是他们仍然必须遵循它们是单个表达式的限制。因此,在函数定义中使用raisetry的解决方案已经完成。不幸的是,函数定义,raise-statement和try -block是语句而不是表达式。

至于我尝试过的任何解决方案。答案还没有。我最接近如何解决这个问题的方法是滥用unittest的断言功能,但我认为这是一个死胡同。

编辑2:为了说清楚,我可以使用一个使用raise - 语句或try - 模块的模块。码。我的目标是获取一些代码并将其转换为等效的单行代码(其中包括我可能正在使用的任何辅助函数)。但是因为我希望这可以在Python的默认安装上工作,所以我只想使用默认库。

4 个答案:

答案 0 :(得分:2)

提出异常:

>>> import warnings
>>> WV = type("WV", (Warning, ValueError), {})
>>> warnings.simplefilter("error", WV)
>>> warnings.warn("wv", WV)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
__main__.WV: wv

捕捉异常:

>>> import unittest
>>> res = type("TR", (unittest.TestResult, ), dict(addError=lambda self, test, err: setattr(self, '_last_err', err)))()
>>> unittest.FunctionTestCase(lambda: [][0])(res)
>>> res._last_err
(<type 'exceptions.IndexError'>, IndexError('list index out of range',), <traceback object at 0x2b4358e69950>)

请注意,warnings方法仅适用于从Warning派生的异常,但您应该始终能够多次继承;这是一个例子:

>>> WS = type("WS", (Warning, StopIteration), {})
>>> warnings.simplefilter("error", WS)
>>> list(type("R", (object,), dict(__init__=lambda self, stop: (setattr(self, 'stop', stop), setattr(self, 'i', 0), None)[-1], __iter__=lambda self: self, next=lambda self: (self.i, setattr(self, 'i', self.i + 1))[0] if self.i < self.stop else warnings.warn("Stop", WS)))(5))
[0, 1, 2, 3, 4]

答案 1 :(得分:1)

您可以定义自己的功能来执行此操作:

def raise_exception(ex):
    raise ex

def check_exception(f, *args, **kwargs):
    try:
        return False, f(*args, **kwargs)
    except Exception as e:
        return True, e

答案 2 :(得分:1)

This answer表明通常无法用表达式捕获异常。我也很确定在不使用raise的情况下不可能引发任意异常。 (您可以使用1/0dict['keyThatWillNeverExist']等表达式生成某些特定异常,但不能使用任意异常信息生成任何异常。)

language reference说:

  

Python解释器在检测到运行时错误(例如除以零)时引发异常。 Python程序也可以使用raise语句显式引发异常。使用try ... except语句指定异常处理程序。

虽然这并不排除语言规范的某个黑暗角落允许以其他方式引发异常的可能性,但语句非常简单:用raise引发异常并用{{1}捕获它们}。

请注意,使用unittest或任何其他Python库不太可能是您意义上的真正解决方案,因为unittest包含使用try / except编写的Python函数。所以,如果你对使用unittest感到满意,你应该可以编写自己的函数了。

我想通过“欺骗”和编写一个C扩展来实现你的目标是可能的,它可以提供你想要的功能。但这并没有真正将其转换为等效的Python代码。

答案 3 :(得分:0)

您询问如何在不使用raise的情况下引发异常,并在不使用except的情况下捕获异常。您不愿意使用这些语句是因为您不能在一行代码中使用多个语句,并且您可以将Python模块编译为oneliner。

简短回答:嗯,你做不到。

即使你可以,为什么你呢?这是一个完全没有意义的努力。代码不是更快,甚至更小,因为它在一行中。它也违背了Python的想法。如果你想混淆它,有更好的方法,包括将其编译为字节码。

更长的答案:

你可以独立于Python异常实现自己的异常系统,但这会非常慢,而且仍然无法捕获Python异常,所以它在你的情况下没用。

对于raise - 语句,你可以将raise语句重新实现为C语言中的一个函数,但是你似乎认为这是作弊,而且我也看不出它是如何与其他语句一起实现的。语句,例如except

你也可以在一个单独的模块中将一些语句转移到函数中,但这当然不再是任何有意义的单行模块,并且并非所有语句都像这样容易包装,{{1}这是最相关的案例。你必须包装整个except块,但结果函数反过来也只将表达式作为参数,所以你必须将块提取到函数中,你最终需要基本上重新 - 实现大多数Python作为无语句语言,这很愚蠢。并且您最终将帮助函数放在一个单独的模块中,您不想这样做。

因此,关于如何在不使用try/except的情况下引发异常并在不使用raise的情况下捕获异常的问题的答案是“你没有”