Why can't I inherit from dict AND Exception in Python?

廉价感情. 提交于 2019-12-17 16:17:11

问题


I got the following class :

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

What the heck ?

And the worst is that I can't try super() since Exception are old based class...

EDIT : And, yes, I've tried to switch the order of inheritance / init.

EDIT2 : I am using CPython 2.4 on Ubuntu8.10. You newer know is this kind of infos is usefull ;-). Anyway, this little riddle has shut the mouth of 3 of my collegues. You'd be my best-friend-of-the day...


回答1:


Both Exception and dict are implemented in C.

I think you can test this the follwing way:

>>> class C(object): pass
...
>>> '__module__' in C.__dict__
True
>>> '__module__' in dict.__dict__
False
>>> '__module__' in Exception.__dict__
False

Since Exception and dict have different ideas of how to store their data internally, they are not compatible and thus you cannot inherit from both at the same time.

In later versions of Python you should get an Exception the moment you try to define the class:

>>> 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:


What's wrong with this?

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)

This is an Exception, and it contains other exceptions in an internal dictionary named failures.

Could you update your problem to list some some specific thing this can't do?

try:
    raise ConstraintFailure( x=ValueError, y=Exception )
except ConstraintFailure, e:
    print e['x']
    print e['y']


<type 'exceptions.ValueError'>
<type 'exceptions.Exception'>



回答3:


No reason but a solution

For the moment I still don't know the why, but I bypass it using UserDict.UserDict. It's slower since it's pure Python, but I don't think on this part of the app it will be troublesome.

Still interested about the answer anyway ;-)




回答4:


What version of Python?

In 2.5.1, I can't even define a class the inherits from both dict and 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

If you're using an older version, maybe it doesn't do this check during type definition and the conflict causes oddities later on.




回答5:


I am almost certain that with 2.4 problem is caused by exceptions being old style classes.

$ 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'>

In both versions as the message says exceptions can be classes, instances (of old style classes) or strings (deprecated).

From version 2.5 exception hierarchy is based on new style classes finally. And instances of new style classes which inherit from BaseException are now allowed too. But in 2.4 multiple inheritance from Exception (old style class) and dict (new style class) results in new style class which is not allowed as exception (mixing old and new style classes is probably bad anyway).




回答6:


Use collections.UserDict to avoid metaclass conflicts:

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()


来源:https://stackoverflow.com/questions/309129/why-cant-i-inherit-from-dict-and-exception-in-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!