为什么处理多个异常需要一个元组而不是一个列表? [英] Why does handling multiple exceptions require a tuple, but not a list?

查看:81
本文介绍了为什么处理多个异常需要一个元组而不是一个列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下示例:

  def main_list(error_type):

试试:
if error_type =='runtime':
引发RuntimeError( list error)
if error_type =='valueerror':
引发ValueError( list error)

,除了[RuntimeError,ValueError],例如e:
print str(e)

def main_tuple(error_type):

try:
如果error_type =='运行时':
提高RuntimeError(元组错误)
如果error_type =='valueerror':
提高ValueError(元组错误)

((RuntimeError,ValueError)除外,例如e:
print str(e)


main_tuple('runtime')
main_tuple('valueerror')

main_list('运行时')
main_list('valueerror')

元组是处理多种异常类型的正确方法。对多个异常类型使用列表不会导致任何异常。



我想知道为什么Python语法需要一个用于多个异常类型的元组。 文档说它使用一个元组,所以也许它只是从未实现使用列表而不是元组。



在我看来,至少在概念上也可以在这种情况下使用列表。



在这种情况下,为什么Python使用元组而不是列表吗?

解决方案


为什么要处理多个异常需要一个元组而不是一个列表?


错误处理方式为C,在其他类型检查和异常处理之前,将类型检查用于元组的特殊情况,以便可以捕获多种类型的异常。



至少一位Python核心开发人员主张使用异常处理进行控制流。



看来,核心开发团队并未专门解决将其扩展为允许集合或列表的问题。 ,但是如果可以找到它,我将很乐意引用它。关于 Python邮件列表推测得很多(这里的另一个答案引用了一个答案)。



在进行了以下分析之后,并在邮件列表讨论的背景下,我认为推理是显而易见的。我不建议添加其他容器。



列出失败与元组的演示



  exceptions = TypeError,RuntimeError 
list_of_exceptions = list(exceptions)

捕获异常元组确实起作用:

  try:
引发TypeError('foo')
,但错误除外:
print(error)

输出:

  foo 

但是捕获例外列表不起作用:

 尝试:
引发TypeError('foo')
,但list_of_exceptions除外:
print(error)

打印:

  
追溯(最近一次通话):
< module>中的文件< stdin>,第2行。
TypeError:foo

在处理上述异常期间,发生了另一个异常:

追溯(最近一次调用为最新):
文件< < stdin>,第3行,< module>
TypeError:不允许捕获不继承自BaseException的类

这表明我们正在对元组的特殊情况进行类型检查。当然,添加其他类型来检查代码会使代码变慢,并且核心开发人员一直在说使用异常是一件好事在Python中处理控制流已有一段时间了。



源代码分析



对源代码的分析与上述结论相符。



语法



对于Python的语法或解析。它将接受任何表达式。因此,任何导致Exception或Exception元组的表达式都应该合法。



反汇编



如果我们反汇编一个在Python 3中执行此操作的函数,我们看到它看起来将异常与比较操作相匹配。

  def catch(例外):
尝试:
引发异常
除以下情况:
pass

import dis
dis.dis(catch)

哪些输出:

  2 0 SETUP_EXCEPT 10(to 13)

3 3 LOAD_GLOBAL 0(例外)
6 RAISE_VARARGS 1
9 POP_BLOCK
10 JUMP_FORWARD 18(至31)

4 >> 13 DUP_TOP
14 LOAD_FAST 0(例外)
17 COMPARE_OP 10(例外匹配)
...

这使我们进入Python解释器。



内部控制流-CPython的实现细节



CPython控件流首先检查该值是否是一个元组。如果是这样,
使用元组特定的代码遍历元组-寻找该值是一个例外:

  case PyCmp_EXC_MATCH:
if(PyTuple_Check(w)){
Py_ssize_t i,length;
长度= PyTuple_Size(w);
for(i = 0; i< length; i + = 1){
PyObject * exc = PyTuple_GET_ITEM(w,i);
if(!PyExceptionClass_Check(exc)){
_PyErr_SetString(tstate,PyExc_TypeError,
CANNOT_CATCH_MSG);
返回NULL;
}
}
}
else {
if(!PyExceptionClass_Check(w)){
_PyErr_SetString(tstate,PyExc_TypeError,
CANNOT_CATCH_MSG) ;
返回NULL;
}
}
res = PyErr_GivenExceptionMatches(v,w);
休息时间;

添加另一种类型将需要更多的内部控制流,从而减慢Python解释器内部的控制流。 / p>

Python容器的大小



Tuples是轻量级的指针数组。列表也是如此,但是可以为它们分配额外的空间,以便您可以快速添加它们(直到它们需要变大为止)。在Linux上的Python 3.7.3中:

 >>>从sys import getsizeof 
>> getsizeof((1,2,3))
72
>> getsizeof([1,2,3])
88

集合占用更多空间因为它们是哈希表。它们既包含其所包含对象的哈希值,又具有指向其所指向对象的指针。



结论



这是供CPython核心开发团队讨论和决定的。



但是我的结论是,即使在C级别通过检查其他类型来减慢Python中的控制流速度,也会与在Python中为控制流使用异常处理的策略背道而驰。模块。



经过上述推理,我不建议他们添加此内容。


Consider the following example:

def main_list(error_type):

    try:
        if error_type == 'runtime':
            raise RuntimeError("list error")
        if error_type == 'valueerror':
            raise ValueError("list error")

    except [RuntimeError, ValueError] as e:
        print str(e)

def main_tuple(error_type):

    try:
        if error_type == 'runtime':
            raise RuntimeError("tuple error")
        if error_type == 'valueerror':
            raise ValueError("tuple error")

    except (RuntimeError, ValueError) as e:
        print str(e)


main_tuple('runtime')
main_tuple('valueerror')

main_list('runtime')
main_list('valueerror')

The tuple is the correct way to handle multiple exception types. Using a list for the multiple exception types causes neither to be handled.

I am wondering why Python syntax requires a tuple for multiple exception types. The docs say that it uses a tuple, so perhaps it is just "never was implemented using a list instead of a tuple."

It seems reasonable to me that a list could also be used in this situation, conceptually at least.

Is there any reason why Python uses a tuple instead of a list for this situation?

解决方案

Why does handling multiple exceptions require a tuple and not a list?

The error handling, written in C, uses type checking for the special case of a tuple, before other type-checking and exception handling, so that multiple types of exceptions may be caught.

At least one Python core developer advocates using exception handling for control flow. Adding lists as an additional type to check would work against this strategy.

It appears that expanding this to allow for sets or lists has not been specifically addressed by the core development team, though I will gladly reference it if it can be found. There has been a discussion on the Python mailing list that speculates quite a lot (another answer here quotes one response at length).

After performing the below analysis, and in the context of the mailing list discussion, I think the reasoning is obvious. I do not suggest proposing to add other containers.

Demonstration that lists fail versus tuples

exceptions = TypeError, RuntimeError
list_of_exceptions = list(exceptions)

Catching the tuple of exceptions does work:

try:
    raise TypeError('foo')
except exceptions as error:
    print(error)

outputs:

foo

But catching the list of exceptions does not work:

try:
    raise TypeError('foo')
except list_of_exceptions as error:
    print(error)

prints:


Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: foo

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
TypeError: catching classes that do not inherit from BaseException is not allowed

This demonstrates that we are doing type checking for the special case of a tuple. It would certainly make the code slower to add another type to check for, and the core developers have been saying that it's a good thing to use exception handling for control flow in Python for a while now.

Source Code analysis

An analysis of the source agrees with this above conclusion.

Grammar

This is not an issue for Python's grammar or parsing. It will accept any expression. Thus any expression that results in an Exception or tuple of Exceptions should be legal.

Disassembly

If we disassemble a function that does this in Python 3, we see that it looks to match an exception with a comparison operation.

def catch(exceptions):
    try:
        raise Exception
    except exceptions:
        pass

import dis
dis.dis(catch)

Which outputs:

      2           0 SETUP_EXCEPT            10 (to 13)

      3           3 LOAD_GLOBAL              0 (Exception)
                  6 RAISE_VARARGS            1
                  9 POP_BLOCK
                 10 JUMP_FORWARD            18 (to 31)

      4     >>   13 DUP_TOP
                 14 LOAD_FAST                0 (exceptions)
                 17 COMPARE_OP              10 (exception match)
    ...

This leads us inside the Python interpreter.

Internal control flow - CPython's implementation details

The CPython control flow first checks for if the value is a tuple. If so, it iterates through the tuple using tuple specific code - looking for the value to be a Exception:

case PyCmp_EXC_MATCH:
    if (PyTuple_Check(w)) {
        Py_ssize_t i, length;
        length = PyTuple_Size(w);
        for (i = 0; i < length; i += 1) {
            PyObject *exc = PyTuple_GET_ITEM(w, i);
            if (!PyExceptionClass_Check(exc)) {
                _PyErr_SetString(tstate, PyExc_TypeError,
                                 CANNOT_CATCH_MSG);
                return NULL;
            }
        }
    }
    else {
        if (!PyExceptionClass_Check(w)) {
            _PyErr_SetString(tstate, PyExc_TypeError,
                             CANNOT_CATCH_MSG);
            return NULL;
        }
    }
    res = PyErr_GivenExceptionMatches(v, w);
    break;

Adding another type would require more internal control flow, slowing down control flow inside of the Python interpreter.

Sizes of Python Containers

Tuples are lightweight arrays of pointers. So are lists, but they may be allocated extra space so that you may quickly add to them (up to the point they need to get larger). In Python 3.7.3 on Linux:

>>> from sys import getsizeof
>>> getsizeof((1,2,3))
72
>>> getsizeof([1,2,3])
88

Sets take up even more space because they are hash tables. They have both a hash of the object they contain as well as a pointer to that which they point to.

Conclusion

This is for the CPython core development team to debate and decide.

But my conclusion is that slowing down control flow in Python by checking for other types even at the C level would work against the strategy of using exception handling for control flow in Python modules.

After reasoning through the above, I would not propose that they add this.

这篇关于为什么处理多个异常需要一个元组而不是一个列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆