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

2024-03-18

考虑以下示例:

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')

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

我想知道为什么Python语法requires多个异常类型的元组。这docs https://docs.python.org/2/tutorial/errors.html#handling-exceptions说它使用一个元组,所以也许它只是“从未使用列表而不是元组来实现”。

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

对于这种情况,Python 使用元组而不是列表有什么原因吗?


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

用 C 编写的错误处理在其他类型检查和异常处理之前对元组的特殊情况使用类型检查,以便可以捕获多种类型的异常。

至少一位 Python 核心开发人员提倡使用异常处理来控制流。添加列表作为附加类型进行检查将违背此策略。

核心开发团队似乎还没有专门解决扩展它以允许集合或列表的问题,尽管如果可以找到的话我会很乐意引用它。曾经有过一场讨论Python 邮件列表 https://mail.python.org/pipermail/python-list/2012-January/thread.html#619107这推测了很多(这里的另一个答案详细引用了一个答案)。

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

列表与元组失败的演示

exceptions = TypeError, RuntimeError
list_of_exceptions = list(exceptions)

捕获异常元组确实有效:

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

outputs:

foo

但是捕获异常列表不起作用:

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

这表明我们正在对元组的特殊情况进行类型检查。添加另一种类型来检查肯定会使代码变慢,核心开发人员一直在说这是一件好事在 Python 中使用异常处理来控制流 https://stackoverflow.com/a/16138864/541136有一段时间了。

源码分析

对消息来源的分析也同意上述结论。

Grammar

这对于 Python 来说不是问题grammar https://docs.python.org/3/reference/grammar.html或解析。它会接受任何表达式。因此,任何导致异常或异常元组的表达式都应该是合法的。

拆卸

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

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

import dis
dis.dis(catch)

哪个输出:

      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)
    ...

这引导我们进入 Python 解释器。

内部控制流程——CPython的实现细节

首先是 CPython 控制流程检查该值是否是元组。 https://github.com/python/cpython/blob/master/Python/ceval.c#L5108如果是这样, 它使用元组特定代码迭代元组 - 查找异常值:

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;

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

Python 容器的大小

元组是轻量级的指针数组 https://stackoverflow.com/a/30316760/541136。列表也是如此,但它们可能会被分配额外的空间,以便您可以快速添加它们(直到它们需要变得更大)。在 Linux 上的 Python 3.7.3 中:

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

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

结论

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

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

经过以上推理,我不建议他们添加这个。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么处理多个异常需要元组而不是列表? 的相关文章

随机推荐

  • Angular 2 可以沿着路由器传递复杂的对象吗?

    是否可以通过路由器发送复杂的对象 这是我正在做和尝试做的事情 在搜索页面中 用户可以单击结果之一上的按钮 该按钮将调用导致该行触发的方法 this router navigate profile detail selection The s
  • 使用 SAX 和 Java 生成 XML [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有人知道使用 SAX 框架 或类似框架 和 Java 编写 XML 的好教程 或者有一个好例子 吗 搜
  • 将 RGB 转换为 HSV 以及将 HSV 转换为 RGB(范围为 0-255)的算法

    我正在寻找从 RGB 到 HSV 的色彩空间转换器 特别是两种色彩空间的 0 到 255 范围 我已经使用它们很长时间了 此时不知道它们来自哪里 请注意 输入和输出 除了以度为单位的角度 都在 0 到 1 0 的范围内 注意 此代码不对输入
  • Java I/O - 重用InputStream对象

    无论如何 是否可以通过更改其内容来重用输入流 没有新的声明 例如 我能够做到非常接近我的要求 但还不够在下面的代码中我使用SequenceInputStream 每次我添加一个新的InputStream到那个顺序 但我想通过使用相同的 in
  • 在 iOS 7 中调整 ModalViewController 的大小并将其放置在中心

    我试图通过减少其宽度和高度来在 iPad 上显示 modalView 但问题是它不是中心对齐的 在 iOS 6 中它曾经工作得很好 但在 iOS 7 中它不是中心对齐的 下面是我的代码 m helpQA HelpQAViewControll
  • 如何让 Rust 单例的析构函数运行?

    这些是我所知道的在 Rust 中创建单例的方法 macro use extern crate lazy static use std sync Mutex Once ONCE INIT derive Debug struct A usize
  • 最小宽度布局。 Nexus 7 中的错误?

    使用layout swdp 限定符时 我得到的结果如附件中所示 sw 限定符应该意味着最小尺寸必须匹配或大于限定符 这似乎不适用于 Nexus 7 运行 4 2 1 我是否对最小宽度限定符的作用感到困惑 或者 N7 报告错误 为了重现我的测
  • Nuxt3 useAsyncData 无法在已安装的生命周期挂钩上工作

    我仍然对我在这里做错了什么感到有点困惑 本质上我有一个 vue 组件 我想在安装元素后异步加载一些数据 我正在使用 NUXT 3 和组合 API 看起来 onMounted 在渲染之前触发 并且没有正确接收数据 如果我将
  • 如何避免课堂自用

    我有以下课程 public class MyClass public void deleteOrganization Organization organization Delete organization Delete related
  • 为什么 ImageIO.read() 这么慢?

    所以我试图从流中获取 PNG 图像 image ImageIO read inputStream 这段代码运行了十秒钟 我认为问题出在缓慢的InputStream上 所以我尝试先将它加载到缓冲区中 byte bytes inputStrea
  • 如何使用JS通过更改另一个字段值来自动更改一个输入字段值

    有两个输入字段total amount delivery charge total amount字段已经有一个值 现在我希望当我输入一些值时delivery charge字段将改变total amount字段的值 Suppose total
  • 如何清除powershell中的变量内容

    我昨天刚开始学习powershell powershell 非常新 我创建了很多变量用于测试目的 以下是我关于变量的问题 如何列出我之前创建的所有变量 那么如何清除变量的所有内容呢 如何移除 删除变量 如何列出我之前创建的所有变量 这会获取
  • Golang:如何创建未知(动态)地图长度

    我可以通过创建 静态 地图 type m map int map int map int bool 但 键 的长度是动态的 unknown len m 1 2 3 4 2 0 true or unk len m 1 2 3 4 true 我
  • 在 Android Kotlin 中查找日期之间的天数差异

    所以 我已经在 google 上搜索 其中大多数使用 Java 有一个使用 Kotlin 并且经常使用与我使用的时间格式不同的时间格式 yyyy mm dd HH mm ss 所以我尝试编码并卡住了 所以 这是代码 import java
  • Spark中使用Map替换列值

    我必须将列列表映射到 Spark 数据集中的另一列 想像这样 val translationMap Map Column Column Map lit foo gt lit bar lit baz gt lit bab 我有一个像这样的数据
  • 如何在R中将时差转换为分钟?

    我有以下程序 timeStart lt Sys time timeEnd lt Sys time difference lt timeEnd timeStart anyVector lt c difference 最后我需要将该数据放入向量
  • 类方法作为 scipy.optimize.curve_fit 的模型函数

    说明书上有这样一句话curve fit that 模型函数 f x 它必须将自变量作为第一个参数 并将参数作为单独的剩余参数进行拟合 但是 我想使用该类的方法作为模型函数 其定义为 def model fun self x par 因此 如
  • 在 WPF 中重复背景画笔

    谢谢你 这个问题与这里这个古老的 未解答的问题非常相似 如何将类似笔记本的线条绘制为 TextBox 背景 https stackoverflow com questions 4041642 wpf how to paint noteboo
  • 带有参数列表的简洁查询

    我正在尝试使用 Dapper 运行带有一组已知参数的查询 但带有这些参数的值列表 我想做的一个简单的例子是 DateTime endDate DateTime Now DateTime startDate endDate AddHours
  • 为什么处理多个异常需要元组而不是列表?

    考虑以下示例 def main list error type try if error type runtime raise RuntimeError list error if error type valueerror raise V