为什么异常代理将str\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu?

2024-05-20 18:43:36 发布

您现在位置:Python中文网/ 问答频道 /正文

为什么打印异常实例会打印exc.args的值,而不是直接表示exc?文档称之为convenience,但实际上这是一个不便。你知道吗

无法区分*args和元组之间的区别:

>>> print(Exception(123, 456))
(123, 456)
>>> print(Exception((123, 456)))
(123, 456)

无法可靠识别类型:

>>> print(Exception('123'))
123
>>> print(Exception(123))
123

还有可爱的“隐形”例外:

>>> print(Exception())

>>> 

除非你明确要求不继承:

>>> class MyError(Exception):
...     """an error in MyLibrary"""
...     
>>> print(MyError())

>>> 

如果您忘记专门用repr记录错误实例,那么这可能是一个真正的问题—日志文件中的默认字符串表示形式已经不可逆转地丢失了信息。你知道吗

这种奇怪的Exception.__str__实现的理由是什么?假设用户想要打印exc.args,那么他们应该只打印exc.args?你知道吗


Tags: 实例文档an隐形类型exceptionargsclass
1条回答
网友
1楼 · 发布于 2024-05-20 18:43:36

BaseException.__str__本可以用一种与python3向后不兼容的方式进行修复,以至少包含异常的类型,但是也许没有人注意到它是一个应该被修复的东西。你知道吗

目前的实施可追溯到PEP 0352,其中提供了基本原理:

No restriction is placed upon what may be passed in for args for backwards-compatibility reasons. In practice, though, only a single string argument should be used. This keeps the string representation of the exception to be a useful message about the exception that is human-readable; this is why the __str__ method special-cases on length-1 args value. Including programmatic information (e.g., an error code number) should be stored as a separate attribute in a subclass.

当然,Python本身在许多情况下打破了有用的人类可读消息的原则—例如,KeyError的字符串化是找不到的键,这会导致调试消息,如

An error occurred: 42

之所以str(e)本质上是str(e.args)str(e.args[0])最初是与python1.0向后兼容的。在python1.0中,引发异常(如ValueError)的语法应该是:

>>> raise ValueError, 'x must be positive'
Traceback (innermost last):
  File "<stdin>", line 1
ValueError: x must be positive

Python保留了与1.0到2.7的向后兼容性,因此您可以在Python2.7中运行大多数Python1.0程序(就像您永远不会那样):

>>> raise ValueError, 'x must be positive'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: x must be positive

同样,在python1.0中,您可以用

>>> try:
...     raise ValueError, 'foo'
... except ValueError, e:
...     print 'Got ValueError', e

在Python2.7中工作不变。你知道吗

但是内部工作机制发生了变化:在python1.0.1中,ValueError是一个字符串,带有值。。。'ValueError'

>>> ValueError, type(ValueError)
('ValueError', <type 'string'>)

根本没有异常类,您只能raise使用字符串作为鉴别器的单个参数或元组:

>>> class MyCustomException: 
...     pass
...   
>>> raise MyCustomException, 'my custom exception'
Traceback (innermost last):
  File "<stdin>", line 1
TypeError: exceptions must be strings

也可以给出一个元组作为参数:

>>> raise ValueError, ('invalid value for x', 42)
Traceback (innermost last):
  File "<stdin>", line 1
ValueError: ('invalid value for x', 42)

如果在python1.0中捕捉到这个“异常”,那么在e中得到的是:

>>> try:
...     raise ValueError, ('invalid value for x', 42)
... except ValueError, e:
...     print e, type(e)
... 
('invalid value for x', 42) 42 <type 'tuple'>

一个元组!你知道吗

让我们试试Python 2.7中的代码:

>>> try:
...     raise ValueError, ('invalid value for x', 42)
... except ValueError, e:
...     print e, e[1], type(e)
... 
('invalid value for x', 42) 42 <type 'exceptions.ValueError'>

输出看起来完全相同,只是值的类型不同;它以前是tuple,现在是一个异常。。。Exception不仅将__str__委托给args成员,而且它还像元组那样支持索引,以及解包、迭代等:

Python 2.7版

>>> a, b, c = ValueError(1, 2, 3)
>>> print a, b, c
1 2 3

所有这些黑客都是为了保持向后兼容性。你知道吗

python2.7行为来自于PEP 0352中引入的BaseException类;pep0352最初是在python2.5中实现的。你知道吗


在Python3中,旧语法被删除了—不能用raise discriminator, (arg, um, ents)引发异常;except只能使用Exception as e语法。你知道吗

pep0352讨论了如何放弃对BaseException的多个参数的支持:

It was decided that it would be better to deprecate the message attribute in Python 2.6 (and remove it in Python 2.7 and Python 3.0) and consider a more long-term transition strategy in Python 3.0 to remove multiple-argument support in BaseException in preference of accepting only a single argument. Thus the introduction of message and the original deprecation of args has been retracted.

args的这种贬低似乎被遗忘了,因为它仍然存在于python3.7中,并且是访问许多内置异常的参数的唯一方法。同样地,__str__不再需要委托给args,实际上可以将BaseException.__repr__别名化,从而提供更好、明确的表示:

>>> BaseException.__str__(ValueError('foo', 'bar', 'baz'))
"('foo', 'bar', 'baz')"
>>> BaseException.__repr__(ValueError('foo', 'bar', 'baz'))
"ValueError('foo', 'bar', 'baz')"

但没人考虑过。你知道吗


另外,异常的repr很有用-下次尝试用!r格式打印异常:

print(f'Oops, I got a {e!r}')

结果是

ZeroDivisionError('division by zero',)

正在输出。你知道吗

相关问题 更多 >