Подтвердить что ты не робот

Почему я не могу унаследовать от 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 моих коллег. Ты будешь моим лучшим другом дня...

4b9b3361

Ответ 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()