设置
假设我有一个 Snit
:
class Snit(): pass
还有一个 Snot
,它包含对最多四个 Snit
的弱引用:
import weakref
class Snot():
def __init__(self,s1=None,s2=None,s3=None,s4=None):
self.s1 = weakref.ref(s1)
self.s2 = weakref.ref(s2)
self.s3 = weakref.ref(s3)
self.s4 = weakref.ref(s4)
我还有一个Snot
工厂:
def snot_factory(snits,w,x,y,z):
return Snot(snits[w],snits[x],snits[y],snits[z])
还有 Snit
的 list
(可以说是 snit_list
):
snit_list = []
for i in range(12):
snit_list.append(Snit())
现在,我使用 snit_list
中的 Snit
制作了一个 Snot
列表:
snot_list = []
for i in range(3):
snot_list.append(snot_factory(snit_list[4*i],snit_list[4*i+1],snit_list[4*i+2],snit_list[4*i+3]))
问题
糟糕!我不再需要 snit_list[3]
,所以我将继续删除它:
snit_list.pop(3)
但现在我有一个 Snot
和一个死的 Snit
卡在那里:
snot_list[0].s4 # <weakref at 0x00BlahBlah; dead>
这受不了了!带有死的 Snit
的 Snot
- 显然 - 完全是胡说八道。
所以我真的希望任何对 Snot
的引用至少在它的一个或多个 Snit
已经返回后作为 None
返回被摧毁。但理想情况下,Snot
也从 snot_list
列表中自动删除会更好(len(snot_list)
缩小了删除的 Snot
数量)。
解决这个问题的好方法是什么?
澄清:
Snot
是一个对象,只有当存在一组有效的 Snit
时才应该存在(“有效”意味着它具有相同数量的已定义 Snit
s 它被初始化),具有以下行为:
- 如果
Snot
中的任何一个Snit
消失了(当没有强引用保留时),Snot
也应该消失(这是为什么我将s1
、s2
等设置为弱引用)。请注意,Snot
可能 已使用 4、3、2 或 1 个Snit
进行了初始化。Snit
的数量并不重要,重要的是Snit
的死亡。 - 如果任何一个包含对
Snit
的引用的Snot
消失了,Snit
就会保留下来。 - 可选:删除
Snot
时,包含对Snot
对象的引用的数据结构也会更新(Snot
获取pop
ped) - 可选:当引用某个
Snit
的ALLSnots
消失时,Snit
也会消失, 并且任何包含Snit
的数据结构都会像 #3 中那样更新(Snit
得到pop
ped)。
所以理想的解决方案将允许我进行设置,以便我可以编写如下代码:
snits = get_snits_list(some_input_with_10000_snits)
snots = get_snots_list(some_cross_referenced_input_with_8000_snots)
#e.g.: the input file will say:
#snot number 12 is made of snits 1, 4, 7
#snot number 45 is made of snits 8, 7, 0, 14
do_stuff_with_snits()
snits.pop(7) #snit 7 is common to snot 12 and 45
assert len(snots) == 7998 #snots 12 and 45 have been removed
但是,如果这太难了,我可以:
assert snots[12] == None
assert snots[45] == None
我愿意改变一些事情。例如,如果它使设计更容易,我认为可以删除对 Snit
的弱引用,或者将它们移动到 Snit
的列表中>s 而不是让 Snot
成员成为弱引用(尽管我看不出这些变化会如何改进)。
我还考虑过创建 Snot
子类 - ClearSnot
有 1 个 Snit
,YellowSnot
有 2 个 Snit
、GreenSnot
和 3 个 Snit
等。我不确定这是否会让事情更容易维护,或者更难维护。
最佳答案
没有什么是真正自动的。你需要有一个手动运行的函数来检查死的 Snit
,或者有一个函数是 Snot
的一部分,只要有任何有趣的事情就会被调用到 Snot
以检查并删除死的 Snit
。
例如:
class Snot:
...
def __repr__(self):
# check for and remove any dead Snits
self._remove_dead_snits()
return ...
def _remove_dead_snits(self):
if self.s1() is None:
self.s1 = None
... # and so on and so forth
有趣的部分是为与 Snot
的每个有趣交互添加对 _remove_dead_snits
的调用——例如 __getitem__
、__iter__
,以及您可以用它做的任何其他事情。
实际上,仔细考虑一下,如果每个 Snot
只有四个可能的 Snit
,您可以使用 SnitRef
描述符——这是代码,对您的原始代码进行了一些更改:
import weakref
class Snit(object):
def __init__(self, value):
self.value = value # just for testing
def __repr__(self):
return 'Snit(%r)' % self.value
class SnitRef(object): # 'object' not needed in Python 3
def __get__(self, inst, cls=None):
if inst is None:
return self
return self.ref() # either None or the obj
def __set__(self, inst, obj):
self.ref = weakref.ref(obj)
class Snot(object):
s0 = SnitRef()
s1 = SnitRef()
s2 = SnitRef()
s3 = SnitRef()
def __init__(self,s0=None,s1=None,s2=None,s3=None):
self.s0 = s0
self.s1 = s1
self.s2 = s2
self.s3 = s3
snits = [Snit(0), Snit(1), Snit(2), Snit(3)]
print snits
snot = Snot(*snits)
print(snot.s2)
snits.pop(2)
print snits
print(snot.s2)
运行时:
[Snit(0), Snit(1), Snit(2), Snit(3)]
Snit(2)
[Snit(0), Snit(1), Snit(3)]
None
关于python - 当其中一个属性失效时自动删除类实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27051119/