Почему исключения повторяемы? - PullRequest
11 голосов
/ 03 ноября 2008

Недавно меня укусило нечто неожиданное. Я хотел сделать что-то подобное:

try :
     thing.merge(iterable) # this is an iterable so I add it to the list
except TypeError :
     thing.append(iterable) # this is not iterable, so I add it

Ну, это работало нормально, пока я не передал объект, наследуемый от Exception, который должен был быть добавлен.

К сожалению, Исключение является итеративным. Следующий код не вызывает никаких TypeError:

for x in Exception() :
    print 1

Кто-нибудь знает почему?

Ответы [ 3 ]

11 голосов
/ 03 ноября 2008

Обратите внимание, что происходящее не связано с каким-либо неявным преобразованием строк и т. Д., А потому, что класс Exception реализует __getitem __ () и использует его для возврата значений в кортеже args (ex.args). Это можно увидеть по тому факту, что вы получаете всю строку в качестве первого и единственного элемента в итерации, а не посимвольный результат, который вы получите, если перебираете строку.

Это меня тоже удивило, но, подумав об этом, я предполагаю, что это из-за обратной совместимости. В Python ( pre-1.5 ) отсутствует текущая иерархия исключений классов. Вместо этого генерировались строки с (обычно) аргументом кортежа для любых деталей, которые должны быть переданы в блок обработки. а именно:

try:
    raise "something failed", (42, "some other details")
except "something failed", args:
    errCode, msg = args
    print "something failed.  error code %d: %s" % (errCode, msg)

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

Строковые исключения уже давно определены и исчезают в Python 3. Теперь я проверил, как Python 3 обрабатывает объекты исключений, и похоже, что они там больше не повторяются:

>>> list(Exception("test"))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Exception' object is not iterable

[Редактировать] Проверено поведение python3

3 голосов
/ 03 ноября 2008

НЕ ДЕЙСТВИТЕЛЬНО. Проверьте Брайана.

Хорошо, я только что получил:

for x in Exception("test") :
    print x
   ....:     
   ....:     
test

Не беспокойся; -)

Во всяком случае, это приятно знать.

РЕДАКТИРОВАТЬ: глядя на комментарии, я чувствую, как добавить некоторые объяснения.

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

raise Exception("test") 

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
Exception: test

Справедливо сказать, что сообщение - это то, что определяет Исключение лучше всего, поэтому str () возвращает его:

print Exception("test") 
test

Теперь случается, что исключения неявно преобразуются в строку, когда они используются не в контексте исключения.

Итак, когда я делаю:

for x in Exception("test") :
    print x

Я перебираю строку "test".

И когда я это сделаю:

for x in Exception() :
    print x

Я перебираю пустую строку. Tricky. Потому что, когда дело доходит до моей проблемы:

try :
    thing.merge(ExceptionLikeObject)
except TypeError :
    ...

Это ничего не вызовет, так как ExceptionLikeObject рассматривается как строка.

Хорошо, теперь мы знаем, КАК, но я все еще не ПОЧЕМУ. Может быть, встроенное исключение наследуется от встроенного String? Потому что, насколько я знаю:

  • добавление str не делает объект повторяемым.
  • Я обошел проблему, перегрузив iter , сделав так, что он поднял ошибку TypeError!

Больше не проблема, но остается загадкой.

2 голосов
/ 03 ноября 2008

На самом деле, я все еще не совсем понимаю. Я вижу, что повторение исключения дает вам исходные аргументы исключения, я просто не уверен, почему кто-то этого захочет. Неявная итерация, я думаю, одна из немногих ошибок в Python, которая до сих пор сбивает меня с толку.

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