Исключение TypeError предупреждение иногда показано, иногда нет при использовании метода throw генератора
есть такой код:
class MyException(Exception):
pass
def gen():
for i in range(3):
try:
yield i
except MyException:
print("MyException!")
a = gen()
next(a)
a.throw(MyException)
запуск этого кода:
$ python3.3 main.py
MyException!
$ python3.3 main.py
MyException!
Exception TypeError: TypeError('catching classes that do not inherit from BaseException is not allowed',) in <generator object gen at 0xb712efa4> ignored
$ python3.3 main.py
MyException!
$ python3.3 main.py
MyException!
$ python3.3 main.py
MyException!
Exception TypeError: TypeError('catching classes that do not inherit from BaseException is not allowed',) in <generator object gen at 0xb714afa4> ignored
вещь, которую я не понимаю, почему иногда печатается это Exception TypeError предупреждение. Что-то не так с пользовательским исключением?
3 ответов
вы видите __del__ крюк плохо себя ведет где-то.
на TypeError вызывается при закрытие, поскольку интерпретатор Python выходит, все удаляется и любые исключения бросаются в __del__ крюк деконструктора игнорируется (но are печать).
при выходе Python очищает все в пространстве имен, восстанавливая все до None, но порядок, в котором это происходит, не установлено. Еще работающий генератор закрыт (a.close() вызывается) при удалении, что вызывает GeneratorExit исключение в генераторе, который Python тестирует против вашего except MyException: линии. Если, однако, MyException и уже был очищен и Python видит except None: на TypeError бросили и вы видите, что сообщение напечатано.
вы можете вызвать ошибку, не выходя из Python, добавив:
MyException = None
del a
если вы используете list(a) и потребляют остальную часть генератора или явно закройте генератор с помощью a.close() перед выходом Python и удалением MyException, сообщение об ошибке исчезнет.
другой обходной путь был бы обрабатывать GeneratorExit первый:
def gen():
for i in range(3):
try:
yield i
except GeneratorExit:
return
except MyException:
print("MyException!")
и Python не будет оценивать следующий except обработчик.
ошибка не может быть воспроизведена с Python 3.2 или ранее, поэтому она выглядит как хэш рандомизации (введено в Python 3.3) рандомизирует объекты порядка очищаются; это, безусловно, объясняет, почему вы смотрите ошибку только на некоторые ваших запусков, но не на более ранних запусках Python, где хэш-порядок фиксирован.
обратите внимание, что взаимодействие .__del__() крючки и другие глобальные объекты в Python документируются с большим красным предупреждением в .__del__() документация:
предупреждение: из-за опасных обстоятельств, при которых методы, исключения, возникающие во время их выполнения игнорируется, и предупреждение печатается в
sys.stderrвместо. Кроме того, когда__del__()вызывается в ответ на удаляемый модуль (например, при выполнении программы), другие глобалы, на которые ссылается__del__()метод, возможно, уже был удален или находится в процессе сноса (например, завершение работы импортного оборудования). По этой причине,__del__()методы должны делать абсолютный минимум, необходимый для поддержания внешних инвариантов. Начиная с версии 1.5, Python гарантирует, что globals имя, начинающееся с одного подчеркивания, удаляется из модуля до удаления других глобалов; если других ссылок на такие глобалы не существует, это может помочь в обеспечении доступности импортированных модулей в то время, когда__del__()метод называется.
у меня была такая же ошибка в Python 3.3 В Windows, с той разницей, что я определял исключение в своем собственном файле. Это были мои файлы кода:
$ cat FooError.py
class FooError(Exception):
pass
$ cat application.py
import FooError
try:
raise FooError('Foo not bar!')
Except FooError as e:
print(e)
Это было исключение, которое я получаю:
TypeError: перехват классов, которые не наследуются от BaseException, не допускается.
изменение import FooError to from FooError import * решен вопрос. Вот окончательный код, для ясности саке:
$ cat FooError.py
class FooError(Exception):
pass
$ cat application.py
from FooError import *
try:
raise FooError('Foo not bar!')
Except FooError as e:
print(e)
У меня была та же проблема, но мне не хватало импорта в класс исключений. Таким образом, интерпретатор не разрешил класс в предложении except.
поэтому просто добавьте импорт и, надеюсь, все будет работать.