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_child
和 remove_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()
无法自动检测附加到列表。
这里有两种足够接近的方式。你只需要一个:
一个 ORM-like save(self)
函数。
不是将子项附加到根,而是使用 Pythonic @property
和 setter
.
将根分配给子项
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()
假设我有两个这样的 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_child
和 remove_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()
无法自动检测附加到列表。
这里有两种足够接近的方式。你只需要一个:
一个 ORM-like
save(self)
函数。不是将子项附加到根,而是使用 Pythonic
@property
和setter
. 将根分配给子项
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()