如果第一个元素是异常,为什么提高元组有效?

时间:2012-03-23 09:30:29

标签: python python-2.7 exception tuples

我很难搞清楚这一点,它是关于在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上所述)

有人可以解释第二和第三种情况,并且可能指向我负责解释的解释器中的代码吗?

3 个答案:

答案 0 :(得分:16)

作为documented in the Python 2 referenceraise语句最多需要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()方法一致,这已经不允许这样做。

http://www.python.org/dev/peps/pep-3109/#id17

答案 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)])"