Подтвердить что ты не робот

Почему создание кортежа работает, если первый элемент является Исключением?

Мне сложно определить это, о ошибках, которые могут быть сделаны при создании исключения в Python 2.7:

try:
  raise [1, 2, 3, 4]
except Exception as ex:
  print ex

сообщение здесь: "Исключения должны быть классами старого стиля или производными от BaseException, а не списком". Эта часть в порядке, но когда я меняю ее на кортеж, меня путают:

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

Здесь мы фактически поднимаем Exception (последовательное поведение по сравнению с предыдущим примером, где мы поднимаем int) - я кратко подумал, что это просто альтернативный способ для этого:

try:
  raise Exception, 'a message'
except Exception as ex:
  print ex

Но в этом случае сообщение "сообщение" передается в Исключения ctor (как описано на docs.python.org)

Может кто-нибудь объяснить 2-й и 3-й случаи, и возможно ли указать мне код в интерпретаторе, который отвечает за это?

4b9b3361

Ответ 1

Как задокументировано в ссылке на Python 2, оператор raise требует до 3 выражений для создания возбуждаемого исключения:

повышение_ст.мт :: = "повышение" [выражение ["," выражение ["," выражение]]]

Если первое выражение является кортежем, python рекурсивно "разворачивает" кортеж, беря первый элемент, пока не найдет что-то кроме кортежа. Это поведение удаляется из Python 3 (см. PEP 3109). Следующее является законным:

>>> raise ((Exception, 'ignored'), 'ignored'), 'something', None
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Exception: something

Документация объясняет остальное более подробно, но оператор поднятия ожидает, что первое значение будет классом исключения, второе значение рассматривается как значение исключения (сообщения), а третье значение является обратной трассировкой. 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

Ответ 2

http://docs.python.org/reference/simple_stmts.html#the-raise-statement

<ы > "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, поддержка для добавления кортежей, подобных этому, будет удалена. Это изменение приведет к тому, что подведение итогов в соответствие с методом throw() для объектов-генераторов, которые уже запрещают это.

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

Ответ 3

По-видимому, Python принимает также непустой кортеж для первого выражения в выражении raise, несмотря на документацию (но, как указано в этот 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, мы видим, что в выражении повышения по умолчанию нет кортежа:

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