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

发布于 2024-09-11 00:41:09 字数 491 浏览 1 评论 0原文

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

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

E.g.,

A
|
|\
| \
B  C

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

就目前而言,我可以查看 B.__slots__B.__dict__,但我只能从那里提取 B 的变量名称。

在仅给出 B 的情况下,如何获取 A 的 __slots__/__dict__ ? (或C)。

我知道Python并不像C++和C++那样直接支持转换。它的后代做——

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-

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

不念旧人 2024-09-18 00:41:09

您可以尝试使用 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

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
天暗了我发光 2024-09-18 00:41:09

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

目前你的描述根本没有描述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()

如果您查看运行时对象的字典,那么它自己的变量和属于 的变量之间没有区别它的超类。例如:
dir(b)

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

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

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

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

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.

耳钉梦 2024-09-18 00:41:09

如果您只需要一棵树(而不是菱形继承),有一个简单的方法可以做到。通过分支 [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__

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

If you only need a tree (not diamond shaped inheritance), there is a simple way to do it. Represent the tree by a nested list of branch [object, [children]] and leaves [object, [[]]].

Then, by defining the recursive function:

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

You can get the inheritance tree:

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

Which is easy to serialize since it's only a list. If you want only the names, replace cls by cls.__name__.

For deserialisation, you have to get your class back from text. Please provide details in your question if you want more help for this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文