Search code examples
pythonreflectionintrospection

Recursively walking a Python inheritance tree at run-time


I'm writing some serialization/deserialization code in Python that will read/write an inheritance hierarchy from some JSON. The exact composition will not be known until the request is sent in.

So, I deem the elegant solution to recursively introspect the Python class hierarchy to be emitted and then, on the way back up through the tree, install the correct values in a Python basic type.

E.g.,

A
|
|\
| \
B  C

If I call my "introspect" routine on B, it should return a dict that contains a mapping from all of A's variables to their values, as well as B's variables and their values.

As it now stands, I can look through B.__slots__ or B.__dict__, but I only can pull out B's variable names from there.

How do I get the __slots__/__dict__ of A, given only B? (or C).

I know that python doesn't directly support casting like C++ & its descendants do-


Solution

  • You might try using the type.mro() method to find the method resolution order.

    class A(object):
            pass
    
    class B(A):
            pass
    
    class C(A):
            pass
    
    a = A()
    b = B()
    c = C()
    
    >>> type.mro(type(b))
    [<class '__main__.B'>, <class '__main__.A'>, <type 'object'>]
    >>> type.mro(type(c))
    [<class '__main__.C'>, <class '__main__.A'>, <type 'object'>]
    

    or

    >>> type(b).mro()
    

    Edit: I was thinking you wanted to do something like this...

    >>> A = type("A", (object,), {'a':'A var'})  # create class A
    >>> B = type("B", (A,), {'b':'B var'})       # create class B
    >>> myvar = B()
    
    def getvars(obj):
        ''' return dict where key/value is attribute-name/class-name '''
        retval = dict()
        for i in type(obj).mro():
            for k in i.__dict__:
                if not k.startswith('_'):
                    retval[k] = i.__name__
        return retval
    
    >>> getvars(myvar)
    {'a': 'A', 'b': 'B'}
    
    >>> for i in getvars(myvar):
        print getattr(myvar, i)   # or use setattr to modify the attribute value
    
    A Var
    B Var