我很难搞清楚这一点,它是关于在Python 2.7中引发异常时可以做的错误:
try:
raise [1, 2, 3, 4]
except Exception as ex:
print ex
这里的消息是"异常必须是旧式类或派生自BaseException,而不是list" - 这部分没问题,但是当我把它改成元组时,我感到很困惑:
try:
raise (1, 2, 3, 4)
except Exception as ex:
print ex
这里的消息是"异常必须是旧式类或派生自BaseException,而不是int" - 为什么它被解释为提高int而不是元组?
Futhermore:
try:
raise (Exception, 'a message')
except Exception as ex:
print ex
这里我们实际上是在提升异常(与前面的例子相比,我们提出了一个int的一致行为) - 我简单地认为这只是另一种方式:
try:
raise Exception, 'a message'
except Exception as ex:
print ex
但在这种情况下,'一条消息'正被传递给Exceptions ctor(如docs.python.org上所述)
有人可以解释第二和第三种情况,并且可能指向我负责解释的解释器中的代码吗?
答案 0 :(得分:16)
作为documented in the Python 2 reference,raise
语句最多需要3个表达式来创建引发的异常:
raise_stmt :: =“raise”[expression [“,”expression [“,”expression]]]
如果第一个表达式是一个元组,python将递归地'解包'元组,取第一个元素,直到找到除元组之外的其他元素。从Python 3中删除了此行为(请参阅PEP 3109)。以下是合法的:
>>> raise ((Exception, 'ignored'), 'ignored'), 'something', None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception: something
文档更详细地解释了其余部分,但是raise语句期望第一个值是Exception类,第二个值被视为异常的值(消息),第三个值是回溯。如果缺少,Python会为后两个值填充None
。
如果第一个值是实例,则第二个值必须为None:
>>> raise Exception('something'), 'something', None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: instance exception may not have a separate value
如果您使用超过3个项目的元组,则会引发语法错误:
>>> raise Exception, 'something', None, None
File "<stdin>", line 1
raise Exception, 'something', None, None
^
SyntaxError: invalid syntax
但是,在你的情况下,你既没有提出类也没有实例,所以这就是Python首先发现的错误;如果我使用字符串,它也会抱怨:
>>> raise 'not an exception', 'something', None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: exceptions must be old-style classes or derived from BaseException, not str
正确的语法当然是:
>>> raise Exception, 'something', None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception: something
答案 1 :(得分:3)
http://docs.python.org/reference/simple_stmts.html#the-raise-statement
“raise”[表达式[“,”表达式[“,”表达式]]]
如果没有表达式,则raise重新引发当前作用域中活动的最后一个异常...否则, raise计算表达式以获得三个对象 ,使用None作为省略表达式的值。前两个对象用于确定异常的类型和值。
实际上,我认为python在这里打包解包
try:
raise (ValueError, "foo", ), "bar"
except Exception as e:
print e.message # foo or bar?
但如果确实如此,结果将是“foo”,而不是“bar”。这种行为似乎没有记录在任何地方,只有关于它在py3中被删除的简短说明:
在Python 2中,以下raise语句是合法的
升高((E1,(E2,E3)),E4),V
解释器将元组的第一个元素作为异常类型(递归),使上述完全等同于
提高E1,V
从Python 3.0开始,支持提升这样的元组将被删除。此更改将使raise语句与生成器对象上的throw()方法一致,这已经不允许这样做。
答案 2 :(得分:3)
显然Python尽管有文档(但如this PEP中所述)也接受了一个非空元组的第一个表达式,如果它是一个元组,它会递归地使用它的第一个元素例外。让我给你看一些代码:
>>> raise ValueError, 'sdf', None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: sdf
>>> raise (ValueError, 5), 'sdf', None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: sdf
尽管我在之前的评论中已经说过,但是没有自动解包,因为在下一个例子中字符串没有传递给异常类:
>>> raise (ValueError, 'sdf', None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError
同样使用python ast模块,我们可以看到在raise表达式中默认没有元组:
>>> ast.dump(ast.parse('raise ValueError, "asd"'))
"Module(body=[Raise(type=Name(id='ValueError', ctx=Load()), inst=Str(s='asd'), tback=None)])"
如果我们使用元组,则将其作为类型参数传递:
>>> ast.dump(ast.parse('raise (ValueError, "asd")'))
"Module(body=[Raise(type=Tuple(elts=[Name(id='ValueError', ctx=Load()), Str(s='asd')], ctx=Load()), inst=None, tback=None)])"