代码之家  ›  专栏  ›  技术社区  ›  Paul Nathan

在运行时递归地遍历Python继承树

  •  7
  • Paul Nathan  · 技术社区  · 14 年前

    我正在用python编写一些序列化/反序列化代码,这些代码将从一些JSON读取/写入继承层次结构。在发送请求之前,不会知道确切的组成。

    因此,我认为递归地内省要发出的Python类层次结构的优雅解决方案,然后在返回树的过程中,在Python基本类型中安装正确的值。

    E.g.,
    
    A
    |
    |\
    | \
    B  C
    

    如果我在B上调用我的“内省”例程,它应该返回一个dict,其中包含从A的所有变量到它们的值以及B的变量和它们的值的映射。

    现在看来,我可以看穿 B.__slots__ B.__dict__ ,但我只能从那里提取b的变量名。

    我怎么得到 __slots__ / __dict__ A的,只给B的?(或C)。

    我知道Python并不直接支持像C++这样的子孙,

    3 回复  |  直到 5 年前
        1
  •  11
  •   Ad Hoc    14 年前

    您可以尝试使用type.mro()方法来查找方法的解析顺序。

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

    >>> type(b).mro()
    

    编辑:我以为你想做这样的事……

    >>> 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
    
        2
  •  2
  •   Andrew    14 年前

    也许你可以进一步澄清你在寻找什么?

    目前,您的描述根本无法描述Python。假设在您的示例中,a、b和c是类的名称:

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

    然后可以创建一个运行时实例:

    b = B()
    

    如果您查看运行时对象的字典,那么它本身的变量和属于它的超类的变量之间没有区别。例如: 迪尔(B)

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

    所以你的问题的直接答案是它已经像那样工作了,但是我怀疑这对你没有什么帮助。不显示的是方法,因为它们是类命名空间中的条目,而变量位于对象的命名空间中。如果要在超类中查找方法,请使用前面的回复中描述的mro()调用,然后查看列表中类的名称空间。

    当我四处寻找执行JSON序列化的简单方法时,我在pickle模块中发现了一些有趣的东西。一个建议是,您可能希望pickle/unpickle对象,而不是编写自己的来遍历层次结构。pickle输出是一个ASCII流,您可以更容易地将其来回转换为JSON。PEP 307中有一些起点。

    另一个建议是看一下 __reduce__ 方法,在要序列化的对象上进行尝试,因为它可能是您要查找的对象。

        3
  •  0
  •   Hugo Trentesaux    5 年前

    如果你只需要一棵树(而不是钻石形状的继承),有一个简单的方法可以做到。用嵌套的分支列表表示树 [object, [children]] 树叶 [object, [[]]] .

    然后,通过定义递归函数:

    def classTree(cls): # return all subclasses in form of a tree (nested list)
        return [cls, [[b for c in cls.__subclasses__() for b in classTree(c)]]]
    

    您可以获得继承树:

    class A():
        pass
    class B(A):
        pass
    class C(B):
        pass
    class D(C):
        pass
    class E(B):
        pass
    
    >>> classTree(A)
    [<class 'A'>, [[<class 'B'>, [[<class 'C'>, [[<class 'D'>, [[]]]], <class 'E'>, [[]]]]]]]
    

    这很容易序列化,因为它只是一个列表。如果只需要名称,请替换 cls 通过 cls.__name__ .

    对于反序列化,您必须从文本中获取类。如果您需要更多帮助,请提供问题的详细信息。