Following system colour scheme - Python 增强提案 Selected dark colour scheme - Python 增强提案 Selected light colour scheme - Python 增强提案

Python 增强提案

PEP 352 – 异常的必需超类

作者:
Brett Cannon, Guido van Rossum
状态:
最终版
类型:
标准跟踪
创建日期:
2005年10月27日
Python 版本:
2.5
发布历史:


目录

摘要

在Python 2.4及以前版本中,任何(经典)类都可以作为异常抛出。2.5版本的计划是允许新式类,但这会使问题变得更糟——这意味着*任何*类(或实例)都可以被抛出!这是一个问题,因为它阻止了对异常接口做出任何保证。本PEP建议引入一个新的超类,所有被抛出的对象都必须继承自它。施加此限制将允许存在一个可依赖的异常标准接口。它还为所有异常提供了一个已知的层次结构。

有人可能会反驳说,为特定接口要求一个特定的基类是不符合Python风格的。然而,在异常的特定情况下,有一个很好的理由(python-dev上普遍认同):要求层次结构有助于那些想要*捕获*异常的代码,通过编写except BaseException:而不是except *:,从而可以显式地捕获*所有*异常。[1]

为异常引入一个新的超类也给了我们一个机会,可以将异常层次结构稍微优化一下。就目前而言,内置命名空间中的所有异常都继承自Exception。这是一个问题,因为这包括两个异常(KeyboardInterrupt和SystemExit),它们通常需要从应用程序的异常处理中排除:关闭解释器而不带回溯的默认行为通常比应用程序可能做的任何事情更可取(可能除了那些使用>>>提示符模拟Python交互式命令循环的应用程序)。通过修改,使这两个异常继承自共同的超类而不是Exception,将使人们更容易编写不过度捕获、不捕获应该向上传播的异常的except子句。

本PEP基于PEP 348之前的工作。

要求一个共同的超类

本PEP建议引入一个名为BaseException的新异常,它是一个新式类,只有一个属性,即args。下面是该异常在Python 3.0中的工作代码(它在Python 2.x中的工作方式将在过渡计划一节中介绍)

class BaseException(object):

    """Superclass representing the base of the exception hierarchy.

    Provides an 'args' attribute that contains all arguments passed
    to the constructor.  Suggested practice, though, is that only a
    single string argument be passed to the constructor.

    """

    def __init__(self, *args):
        self.args = args

    def __str__(self):
        if len(self.args) == 1:
            return str(self.args[0])
        else:
            return str(self.args)

    def __repr__(self):
        return "%s(*%s)" % (self.__class__.__name__, repr(self.args))

为了向后兼容,对args可以传入什么没有限制。然而,实际上,只应使用单个字符串参数。这使得异常的字符串表示是一个有用的人类可读的关于异常的消息;这就是为什么__str__方法特殊处理长度为1的args值。包含程序性信息(例如,错误代码号)应作为单独的属性存储在子类中。

raise语句将被修改,要求传递给它的任何对象都必须继承自BaseException。这将确保所有异常都属于以BaseException为锚点的单个层次结构[1]。这也保证了从BaseException继承的基本接口。raise的更改将从Python 3.0开始强制执行(参见下面的过渡计划)。

随着BaseException成为异常层次结构的根,Exception现在将继承自它。

异常层次结构变化

由于异常层次结构现在变得更加重要,因为它有了一个基本的根,因此需要对现有层次结构进行更改。就目前而言,如果一个人想要捕获所有表示错误的异常,*并且*不意味着解释器应该被允许退出,那么您必须在except子句中明确指定除两个异常之外的所有异常,或者单独捕获这两个异常然后重新抛出它们,并让所有其他异常落入一个裸的except子句

except (KeyboardInterrupt, SystemExit):
    raise
except:
    ...

这没有必要地过于明确。本PEP建议将KeyboardInterrupt和SystemExit直接继承自BaseException。

- BaseException
  |- KeyboardInterrupt
  |- SystemExit
  |- Exception
     |- (all other current built-in exceptions)

这样做使得捕获Exception变得更加合理。它将只捕获表示错误的异常。表示解释器应该退出的异常将不会被捕获,因此允许向上传播并允许解释器终止。

KeyboardInterrupt已被移动,因为用户通常期望在按下中断键(通常是Ctrl-C)时应用程序退出。如果人们有过于宽泛的except子句,预期的行为就不会发生。

SystemExit也因类似原因被移动。由于调用sys.exit()时会引发此异常,因此解释器通常应被允许终止。不幸的是,过于宽泛的except子句可能会阻止明确请求的退出发生。

为了确保大多数时候人们捕获Exception,文档和教程的各个部分需要更新,以强烈建议程序员使用Exception。应不鼓励使用裸except子句或直接捕获BaseException,因为KeyboardInterrupt和SystemExit几乎总是应该允许向上层传播。

过渡计划

由于提出了对 Python 的语义更改,因此需要一个过渡计划。目标是最终在 Python 3.0 中使用新的语义,同时为 2.x 代码提供平稳的过渡。计划中提到的所有弃用都将导致在初始弃用之后的版本中移除语义。

这是在2.x系列中实现的BaseException

class BaseException(object):

    """Superclass representing the base of the exception hierarchy.

    The __getitem__ method is provided for backwards-compatibility
    and will be deprecated at some point.  The 'message' attribute
    is also deprecated.

    """

    def __init__(self, *args):
        self.args = args

    def __str__(self):
        return str(self.args[0]
                   if len(self.args) <= 1
                   else self.args)

    def __repr__(self):
        func_args = repr(self.args) if self.args else "()"
        return self.__class__.__name__ + func_args

    def __getitem__(self, index):
        """Index into arguments passed in during instantiation.

        Provided for backwards-compatibility and will be
        deprecated.

        """
        return self.args[index]

    def _get_message(self):
        """Method for 'message' property."""
        warnings.warn("the 'message' attribute has been deprecated "
                        "since Python 2.6")
        return self.args[0] if len(args) == 1 else ''

    message = property(_get_message,
                        doc="access the 'message' attribute; "
                            "deprecated and provided only for "
                            "backwards-compatibility")

Python 2.9中功能的弃用是可选的。这是因为目前尚不清楚Python 2.9(计划是2.x系列的最后一个版本)是否会主动弃用3.0中将不存在的功能。可以想象,2.9中不会使用弃用警告,因为2.9和3.0之间可能存在如此大的差异,以至于2.9在警告方面会过于“嘈杂”。因此,Python 2.9的拟议弃用警告将在该版本开发开始时重新审视,以确定它们是否仍然需要。

  • Python 2.5 [完成]
    • 所有标准异常都成为新式类 [完成]
    • 引入BaseException [完成]
    • Exception、KeyboardInterrupt和SystemExit继承自BaseException [完成]
    • 弃用抛出字符串异常 [完成]
  • Python 2.6 [完成]
    • 弃用捕获字符串异常 [完成]
    • 弃用message属性(参见已撤销的想法)[完成]
  • Python 2.7 [完成]
    • 弃用抛出不继承自BaseException的异常
  • Python 3.0 [完成]
    • 删除上述所有已弃用的内容
      • 字符串异常(抛出和捕获)[完成]
      • 所有异常必须继承自BaseException [完成]
      • 删除__getitem__message [完成]

已撤销的想法

本PEP的先前版本(在 Python 2.5 中实现)在 BaseException 上包含一个“message”属性。其目的是开始向 BaseException 只接受单个参数过渡。这是为了收紧接口,并强制人们在子类中使用属性来携带异常的任意信息,而不是将其全部塞进 args 中。

不幸的是,在 PyCon 2007 sprint [2] 上实现 Python 3.0 中 args 属性的移除时,发现这种过渡非常痛苦,特别是对于 C 扩展模块。因此决定最好在 Python 2.6 中弃用 message 属性(并在 Python 2.7 和 Python 3.0 中移除),并考虑在 Python 3.0 中采取更长期的过渡策略来移除 BaseException 中对多参数的支持,转而只接受单个参数。因此,message 的引入和 args 的原始弃用已被撤回。

参考资料


来源: https://github.com/python/peps/blob/main/peps/pep-0352.rst

上次修改: 2024-12-03 18:09:24 GMT