PEP 352 – 异常的必需父类
- 作者:
- Brett Cannon, Guido van Rossum
- 状态:
- 最终
- 类型:
- 标准跟踪
- 创建:
- 2005-10-27
- Python 版本:
- 2.5
- 历史记录:
摘要
在 Python 2.4 及之前版本中,任何(经典)类都可以作为异常引发。2.5 的计划是允许使用新式类,但这会使问题更严重——这意味着 *任何* 类(或实例)都可以引发!这是一个问题,因为它阻止了对异常接口的任何保证。本 PEP 建议引入一个新的父类,所有引发的对象都必须继承自该父类。强制实施此限制将允许存在一个可以依赖的标准异常接口。它还导致所有异常都必须遵守已知的层次结构。
有人可能会反驳说,为特定接口要求特定的基类是不 Pythonic 的。但是,在异常的特定情况下,有一个很好的理由(这在 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 是程序员想要使用的。基于 KeyboardInterrupt 和 SystemExit 几乎总是应该被允许传播到上层的事实,应不鼓励使用裸 except
子句或直接捕获 BaseException。
过渡计划
由于提议对 Python 进行语义更改,因此需要过渡计划。目标是最终在 Python 3.0 中使用新的语义,同时为 2.x 代码提供平滑的过渡。计划中提到的所有弃用将在首次弃用后的版本中导致语义的删除。
以下是 BaseException 在 2.x 系列中的实现方式
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 [3] 中实现 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
最后修改时间: 2023-09-09 17:39:29 GMT