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


Answers

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

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




PersistentSet in ZODB 3

Why don't you use the persistent set class provided with the BTree libraries in ZODB. There are 4 such classes available. IITreeSet and IOTreeSet manage sets of integers and OITreeSet and OOTreeSet manage set of arbitrary objects. They correspond to the four BTree classes IIBTree, IOBTree, OIBTree and OOBTree respectively. Their advantages over the set implementation built into Python are their fast lookup mechanism (thanx to the underlying BTree) and their persistence support.

Here is some sample code:

>>> from BTrees.IIBTree import IITreeSet, union, intersection
>>> a = IITreeSet([1,2,3])
>>> a
<BTrees._IIBTree.IITreeSet object at 0x00B3FF18>
>>> b = IITreeSet([4,3,2])
>>> list(a)
[1, 2, 3]
>>> list(b)
[2, 3, 4]
>>> union(a,b)
IISet([1, 2, 3, 4])
>>> intersection(a,b)
IISet([2, 3])



For future readings, I just wanted to offer a slight improvement over the already proposed answers...

Custom persistent set class

class PersistentSet(Persistent):

    def __init__(self, *args, **kwargs):
        self._set = set(*args, **kwargs)

    def __getattr__(self, name):
        return getattr(self._set, name)

Persistent set class from the library

from BTrees.OOBTree import OOSet

See also




Subclass of `type` and `tuple`

I can't seem to find an appropriate link to it, but the point is that Python doesn't support multiple inheritence of several built-in types. You can't create a class that inherit both from "type" and "tuple", or from "int" and "str", or most other combinations. There is a mostly internal reason related to the internal layout of the instances: an "int" object's memory layout contains storage for the integer value; but this is incomptible with the layout of a "str" object, which contains storage for the characters. It's not possible to create an instance of a class that would inherit from both, because we don't know which memory layout it should have.

Compare this with the memory layout of an instance of a class that inherits only from object, directly or indirectly. Such an instance only needs storage for a __dict__, which is a dictionary containing the attributes. This works without problem for whatever multiple inheritance diagram.

These two cases have been combined (in Python 2.2) into the following "best-effort" appraoch: inheritence is only possible if there is at most one built-in base type. In this case, the memory layout can start with the layout expected for this built-in type, and contain the __dict__ afterwards to store any attribute required by the other base classes.




Please read the Python reference on Data model, especially about the __new__ special method.

Excerpt from that page (my italics):

__new__() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.

datetime.datetime is also an immutable type.

PS If you think that:

  • an object implemented in C cannot be subclassed, or
  • __init__ doesn't get called for C implemented objects, only __new__

then please try it:

>>> import array
>>> array
<module 'array' (built-in)>
>>> class A(array.array):
    def __init__(self, *args):
        super(array.array, self).__init__(*args)
        print "init is fine for objects implemented in C"

>>> a=A('c')
init is fine for objects implemented in C
>>> 



Tags