Почему я не могу наследовать от dict и исключения в Python? - PullRequest
18 голосов
/ 21 ноября 2008

Я получил следующий класс:

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 (), так как исключения являются старым классом ...

РЕДАКТИРОВАТЬ: И, да, я пытался изменить порядок наследования / init.

EDIT2: я использую CPython 2.4 на Ubuntu8.10. Вы новичок знаете, что эта информация полезна ;-). Во всяком случае, эта маленькая загадка закрыла рот 3 моих коллег. Ты будешь моим лучшим другом дня ...

Ответы [ 6 ]

21 голосов
/ 21 ноября 2008

Оба 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
4 голосов
/ 22 ноября 2008

Что с этим не так?

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 голосов
/ 21 ноября 2008

Нет причин, кроме решения

На данный момент я все еще не знаю почему, но я обхожу это с помощью UserDict.UserDict. Это медленнее, так как это чистый Python, но я не думаю, что в этой части приложения это будет хлопотно.

Все равно интересуюсь ответом; -)

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

Какая версия 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

Если вы используете более старую версию, возможно, она не выполняет эту проверку во время определения типа, и конфликт позже вызывает странности.

0 голосов
/ 17 декабря 2015

Используйте 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()
0 голосов
/ 29 мая 2009

Я почти уверен, что проблема с 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 (класс нового стиля) в результате получается новый класс стилей, который не допускается как исключение (смешивание старых и новых классов стилей в любом случае, вероятно, плохо).

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