Исключение 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.

поэтому просто добавьте импорт и, надеюсь, все будет работать.