自动"根对象" Python中的检测实现?

时间:2018-04-11 01:53:46

标签: python python-3.x pep8

假设我有两个这样的类:

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 = ???

所以我的问题部分已经存在于代码中,但需要澄清一下。假设我从类中创建对象:

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中设计这样的系统的正确方法是什么?

2 个答案:

答案 0 :(得分:1)

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

以下解决方案直接实施将更新add_child根的remove_childChild方法。

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 :(得分:1)

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

以下两种方法足够接近。你只需要一个:

  1. 类似ORM的save(self)函数。

  2. 不要将子项附加到root,而是将root用户分配给具有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()