Почему поднятие кортежа работает, если первый элемент является исключением? - PullRequest
21 голосов
/ 23 марта 2012

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

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

сообщение здесь: «исключения должны быть классами старого стиля илипроизводная от BaseException, not list "- эта часть в порядке, но когда я изменяю ее на кортеж, я запутываюсь:

try:
  raise (1, 2, 3, 4)
except Exception as ex:
  print ex

сообщение здесь" исключения должны быть классами старого стиля или производными отBaseException, а не 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

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

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

Ответы [ 3 ]

16 голосов
/ 23 марта 2012

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

rise_stmt :: = "повысить"[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

В документации объясняется остальное более подробно, но оператор Повышение ожидает, что первое значение будет классом Исключения, второе значение рассматривается как значение исключения (сообщение) и третье значение является следом.Python заполняет None для последних двух значений, если они пропущены.

Если первое значение равно instance , второе значение должно быть 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
3 голосов
/ 23 марта 2012

Очевидно, что 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, мы можем видеть, что в выражении повышения по умолчанию нет кортежа:

>>> 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)])"
3 голосов
/ 23 марта 2012

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

"поднять" [выражение ["," выражение ["," выражение]]]

Если никаких выражений нет, то рейз повторно вызывает последнее исключение, которое было активным в текущей области ... В противном случае повышение оценивает выражения для получения трех объектов , используя None в качестве значения пропущенного выражения. Первые два объекта используются для определения типа и значения исключения.

На самом деле, я думал, что Python распаковывает здесь кортежи

try:
    raise (ValueError, "foo", ), "bar"
except Exception as e:
    print e.message # foo or bar?

но если это так, то результатом будет "foo", а не "bar". Такое поведение, похоже, нигде не задокументировано, есть только краткая заметка о том, что оно пропущено в py3:

В Python 2 следующий оператор повышения допустим

повышение ((E1, (E2, E3)), E4), V

Интерпретатор примет первый элемент кортежа как тип исключения (рекурсивно), делая приведенное выше полностью эквивалентным

повышение E1, V

Начиная с Python 3.0, поддержка повышения кортежей, подобных этой, будет прекращена. Это изменение приведет операторы повышение в соответствие с методом throw () для объектов-генераторов, что уже запрещает это.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...