Login

Inheritance-aware dictionnary

2008-11-14 11:21

In our framework we are using a dictionnary that stores the views available for a model. But what happens if you still want to provide a view for a subclass of a model ?

Of course there is still the default view created through introspection but they are only good-looking for very simple models. Thus, in this case, we decided to provide the view from the inherited parent.

Here is the code for an inheritance-aware dictionnary class.

class ClassDict(dict):
   """A dictionnary subclass that uses classes as keys
   >>> class A(object): pass
   >>> class B(object): pass
   >>> class C(B): pass
   >>> class D(A, C): pass
   >>> class E(D, B): pass
   >>> a = A(); b = B(); c = C(); d = D(); e = E()
   >>> test = ClassDict()
   >>> test[A] = 'A'
   >>> test[D]
   'A'
   >>> try:
   ...    test[b]
   ... except KeyError:
   ...    print 'Yep'
   Yep
   >>> test[B] = 'B'
   >>> test[E]
   'A'
   >>> test[C]
   'B'
   >>> test[c]
   'B'
   >>> del test[A]
   >>> test[d]
   'B'
   """
   def __getitem__(self, key):
       if not isinstance(key, type):
           key = key.__class__
       found = False
       for class_key in key.__mro__:
           try:
               val = super(ClassDict, self).__getitem__(class_key)
               found = True
               break
           except KeyError:
               pass
       if found:
           return val
       raise KeyError
   def __setitem__(self, key, value):
       if not isinstance(key, type):
           key = key.__class__
       super(ClassDict, self).__setitem__(key, value)

This kind of dictionnary will only accept new-style classes, moreover if you use an instance as a key, it will use as the key the class of this instance.

Comments

1. 2008-11-14 19:05 : by Eric

__getitem__ can be made slightly shorter by using the `else` clause of the `for` loop, instead of a `found` variable:

def __getitem__(self, key):
if not isinstance(key, type):
key = key.__class__
for class_key in key.__mro__:
try:
val = super(ClassDict, self).__getitem__(class_key)
break
except KeyError:
pass
else:
raise KeyError
return val

2. 2008-11-17 08:47 : by Nicolas

Thanks Éric.

Post a comment