rfrankel
rfrankel

Reputation: 675

How can I aggregate dictionaries over class hierarchy (Python)

Let's say I have the following class hierarchy:

class A(object):
  _d = {}

class B(A):
  _d = {'b': 1}

class C(A):
  _d = {'b': 2, 'c': 3}

class D(B):
  _d = {'d': 4}

Is there a way to write an @property method d on A which will return the aggregated dictionary over all superclasses of an object? For example, B().d would return {'b': 1}, C().d would return {'b': 2, 'c': 3}, and D().d would return {'b': 1, 'd': 4}.

Upvotes: 1

Views: 463

Answers (3)

aaronasterling
aaronasterling

Reputation: 71004

Use inspect.getmro to skip any concerns with multiple inheritance and avoid the necessity of a recursive call to get all the subclasses.

import inspect

@property
d(self):
    res = {}
    subclasses = inspect.getmro(type(self))
    for cls in reversed(subclasses):
        res.update(gettattr(cls, 'd', {}))
    return res 

Upvotes: 4

John La Rooy
John La Rooy

Reputation: 304147

  @property
  def d(self):
    res = dict(self._d)
    for c in self.__class__.__bases__:
        res.update(getattr(c(),'d',{}))
    return res

Upvotes: 2

Daniel Kluev
Daniel Kluev

Reputation: 11315

Well, straightforward solution is to use __bases__ attribute of the class to traverse inheritance chain. However there are some multiple inheritance issues which may or may not to be a concern for you.

 def d(self):
     res = {}
     def traverse(cls):
         if hasattr(cls, '_d'):
             for k,v in cls._d.iteritems():
                 res[k]=v
             for basecls in cls.__bases__:
                 traverse(basecls)
     for k,v in self._d.iteritems():
         res[k]=v
     traverse(self.__class__)
     return res

>>> D().d()
{'a': 1, 'c': 4, 'b': 2}

Upvotes: 2

Related Questions