Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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-

like image 476
Paul Nathan Avatar asked Jul 23 '10 18:07

Paul Nathan


2 Answers

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
like image 88
Ad Hoc Avatar answered Sep 25 '22 07:09

Ad Hoc


Perhaps you could clarify what you are looking for a bit further?

At the moment your description doesn't describe Python at all. Let's assume that in your example A, B and C are the names of the classes:

class A(object) :
...     def __init__(self) :
...             self.x = 1
class B(A) :
...     def __init__(self) :
...             A.__init__(self)
...             self.y = 1

Then a runtime instance could be created as:

b = B()

If you look at the dictionary of the runtime object then it has no distinction between its own variables and variables belonging to its superclass. So for example : dir(b)

[ ... snip lots of double-underscores ... , 'x', 'y']

So the direct answer to your question is that it works like that already, but I suspect that is not very helpful to you. What does not show up is methods as they are entries in the namespace of the class, while variables are in the namespace of the object. If you want to find methods in superclasses then use the mro() call as described in the earlier reply and then look through the namespaces of the classes in the list.

While I was looking around for simpler ways to do JSON serialisation I found some interesting things in the pickle module. One suggestion is that you might want to pickle / unpickle objects rather than write your own to traverse the hieracrchy. The pickle output is an ASCII stream and it may be easier for you to convert that back and forth to JSON. There are some starting points in PEP 307.

The other suggestion is to take a look at the __reduce__ method, try it on the objects that you want to serialise as it may be what you are looking for.

like image 34
Andrew Avatar answered Sep 23 '22 07:09

Andrew