Python 中的自动 "root object" 检测实​​现?

Automatic "root object" detection implementation in Python?

假设我有两个这样的 classes:

class Root:
    def __init__(self):
        self.children = []

class Child:
    def __init__(self):
        # How do I make this attribute (or property if I must change
        # it to one) automatically reference the root object (if the
        # current object is a an item of root.children)
        self.root = ???

所以我的问题部分已经在代码中,但需要一些说明。假设我像这样从 class 创建对象:

root_obj = Root()
child_obj = Child()
root_obj.children.append(child_obj)

我如何做到 child_obj.root 自动检测当前对象存储在 root_obj 下并引用该对象?这样,如果我稍后决定将 child_obj 存储在新对象 another_root_obj.children 中,那么 child_obj.root 将改为引用该对象。如果我尝试这样做是不可能的,那么在 Python 中设计这样的系统的正确方法是什么?

对象不会跟踪引用它的对象。主要是因为这很少需要,并且会增加垃圾收集的复杂性。所以这是您必须自己实施的行为。

以下解决方案直接实现 add_childremove_child 方法,这些方法将更新 Child 的根。

class Root:
    def __init__(self):
        self.children = set()

    def add_child(self, child):
        self.children.add(child)
        child.roots.add(self)

    def remove_child(self, child):
        if child in self.children:
            self.children.remove(child)
            child.roots.remove(self)

class Child:
    def __init__(self):
        self.roots = set()

root_obj = Root()
child_obj = Child()

root_obj.add_child(child_obj)

child_obj.roots # {<__main__.Root object at 0x000001FDD5406048>}

root_obj.remove_child(child_obj)

child_obj.roots # set()

无法自动检测附加到列表。

这里有两种足够接近的方式。你只需要一个:

  1. 一个 ORM-like save(self) 函数。

  2. 不是将子项附加到根,而是使用 Pythonic @propertysetter.

  3. 将根分配给子项
class Root:
    def __init__(self):
        self.children = []

    #1
    def save(self):
        for child in self.children:
            child.root = self

class Child:
    def __init__(self):
        self.__root = None

    #2
    @property
    def root(self):
        return self.__root

    #2
    @root.setter
    def root(self, root):
        self.__root = root
        if self not in root.children:
            root.children.append(self)

#1 的用法:

root_obj = Root()
child_obj = Child()

root_obj.children.append(child_obj)
root_obj.save()

print(child_obj.root) # <__main__.Root object at 0x05932890>

#2 的用法:

root_obj = Root()
child_obj = Child()

child_obj.root = root_obj

print(root_obj.children) # [<__main__.Child object at 0x060578F0>]

奖金

如果你把两者结合起来,你可以轻松处理:

  • 重新分配,例如child_obj.root = root_obj_2
    • ...包括边缘情况,例如child_obj.root = None
  • 删除,例如root_obj.children.remove(child_obj) 然后 root_obj.save()
class Root:
    def __init__(self):
        self.children = []
        self.__previous_children = []

    def save(self):
        diff = [c for c in self.__previous_children if c not in self.children]
        if len(diff) == 0 and len(self.__previous_children) == len(self.children):
            return
        for child in diff:
            child.root = None
        self.__previous_children = self.children.copy()
        for child in self.children:
            child.root = self

class Child:
    def __init__(self):
        self.__root = None

    @property
    def root(self):
        return self.__root

    @root.setter
    def root(self, root):
        if self.__root == root:
            return
        if self.__root is not None:
            try:
                self.__root.children.remove(self)
                self.__root.save()
            except:
                pass
        self.__root = root
        if root is None:
            return
        if self not in root.children:
            root.children.append(self)
            root.save()