Почему я не могу унаследовать от dict AND Exception в Python?
Я получил следующий класс:
class ConstraintFailureSet(dict, Exception) :
"""
Container for constraint failures. It act as a constraint failure itself
but can contain other constraint failures that can be accessed with a dict syntax.
"""
def __init__(self, **failures) :
dict.__init__(self, failures)
Exception.__init__(self)
print isinstance(ConstraintFailureSet(), Exception)
True
raise ConstraintFailureSet()
TypeError: exceptions must be classes, instances, or strings (deprecated), not ConstraintFailureSet
Что за черт?
И хуже всего то, что я не могу попробовать super(), поскольку Exception являются старыми классами...
EDIT: И, да, я попытался переключить порядок наследования /init.
EDIT2: Я использую CPython 2.4 на Ubuntu8.10. Вы знаете, что это информация, полезная;-). Во всяком случае, эта маленькая загадка закрыла рот 3 моих коллег. Ты будешь моим лучшим другом дня...
Ответы
Ответ 1
Оба Exception
и dict
реализованы в C.
Я думаю, вы можете проверить это следующим образом:
>>> class C(object): pass
...
>>> '__module__' in C.__dict__
True
>>> '__module__' in dict.__dict__
False
>>> '__module__' in Exception.__dict__
False
Так как Exception
и dict
имеют разные представления о том, как хранить свои данные внутри, они несовместимы и, следовательно, вы не можете наследовать оба из них одновременно.
В более поздних версиях Python вы должны получить исключение в момент, когда вы попытаетесь определить класс:
>>> class foo(dict, Exception):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
Ответ 2
Что случилось с этим?
class ConstraintFailure( Exception ):
def __init__( self, **failures ):
self.failures= failures # already a dict, don't need to do anything
def __getitem__( self, key ):
return self.failures.get(key)
Это исключение и содержит другие исключения во внутреннем словаре с именем failures
.
Не могли бы вы обновить свою проблему, чтобы перечислить некоторые конкретные вещи, которые это не может сделать?
try:
raise ConstraintFailure( x=ValueError, y=Exception )
except ConstraintFailure, e:
print e['x']
print e['y']
<type 'exceptions.ValueError'>
<type 'exceptions.Exception'>
Ответ 3
Какая версия Python?
В 2.5.1 я даже не могу определить класс, наследующий от dict
и Exception
:
>>> class foo(dict, Exception):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
multiple bases have instance lay-out conflict
Если вы используете более старую версию, возможно, она не выполняет эту проверку во время определения типа, а конфликт вызывает нечеткость позже.
Ответ 4
Нет причин, кроме решения
На данный момент я все еще не знаю, почему, но я обойду его, используя UserDict.UserDict
. Это медленнее, так как это чистый Python, но я не думаю, что эта часть приложения будет хлопотной.
В любом случае все равно интересуется ответом; -)
Ответ 5
Я почти уверен, что проблема 2.4 связана с исключениями, которые являются классами старого стиля.
$ python2.4
Python 2.4.4 (#1, Feb 19 2009, 09:13:34)
>>> type(dict)
<type 'type'>
>>> type(Exception)
<type 'classobj'>
>>> type(Exception())
<type 'instance'>
$ python2.5
Python 2.5.4 (r254:67916, Feb 17 2009, 23:11:16)
>>> type(Exception)
<type 'type'>
>>> type(Exception())
<type 'exceptions.Exception'>
В обеих версиях, поскольку в сообщении говорится, что исключениями могут быть классы, экземпляры (классов старого стиля) или строки (устаревшие).
Начиная с версии 2.5, иерархия исключений основывается на новых классах стилей. Также допускаются экземпляры новых классов стиля, которые наследуют от BaseException.
Но в 2.4 множественном наследовании от Exception (класс старого стиля) и dict (новый стиль)
приводит к новому классу стиля, который не допускается как исключение (смешение старых и новых классов стилей, вероятно, так или иначе).
Ответ 6
Используйте collections.UserDict
, чтобы избежать конфликтов метакласса:
class ConstraintFailureSet(coll.UserDict, Exception):
"""
Container for constraint failures. It act as a constraint failure itself
but can contain other constraint failures that can be accessed with a dict syntax.
"""
def __init__(self, **failures) :
coll.UserDict.__init__(self, failures)
Exception.__init__(self)
print( isinstance(ConstraintFailureSet(), Exception)) #True
raise ConstraintFailureSet()