当其中一个属性变为死时,自动删除类实例

时间:2014-11-20 22:50:48

标签: python

设置

说我有Snit

class Snit(): pass

还有Snot,其中包含对最多四个Snit s的弱引用:

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

list Snitsnit_list原样:

snit_list = []
for i in range(12):
    snit_list.append(Snit())

现在,我使用Snot中的Snit制作snit_list的列表:

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>

这不能忍受! Snot Snit Snot显然是完全无意义的。

所以我真的希望None的任何引用在其Snit的一个或多个被销毁之后至少返回为Snot。但理想情况下,snot_list自动从len(snot_list)列表中删除也会更好(Snot会因删除的Snot数量而缩小。< / p>

有什么好办法解决这个问题?

澄清:

Snit是一个对象,只有当有一组有效的Snit时才存在(“有效”表示它具有相同数量的已定义Snit s被初始化with),具有以下行为:

  1. 如果Snot中的任何一个Snot消失(当没有强引用时),s1也应该消失(这就是我设置s2的原因},Snot等是弱引用)。请注意,Snit 可以使用4,3,2或1 Snit初始化。 Snit的数量无关紧要,Snot死亡才是最重要的。
  2. 如果包含对Snit的引用的任何一个Snit消失,则Snot仍然存在。
  3. 可选:删除Snot时,包含对Snot对象的引用的数据结构也会更新(pop获取Snots ped)
  4. 可选:当所有引用某个Snit的{​​{1}}消失时,Snit也会消失,任何包含{{{}的数据结构都会消失1}}在#3中更新(Snit得到Snit ped)。
  5. 所以理想的解决方案将允许我进行设置,以便我可以编写如下代码:

    pop

    然而,如果这太难了,我会好的:

    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成员是弱参考(虽然我不知道这些改变中的任何一个会如何改善)。

    我还考虑创建Snot子类 - Snot,其中包含1 ClearSnotSnit包含2 YellowSnot s,Snit包含3 { {1}}等等我不确定这是否会使事情更容易维护,或者更难。

2 个答案:

答案 0 :(得分:2)

没有什么是真正的自动化。您需要拥有一个手动运行的函数来检查死Snit,或者有一个函数是Snot的一部分,只要Snot发生任何有趣的事情就会被调用Snit 1}}检查并删除死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 s。

例如:

_remove_dead_snits

有趣的部分是将Snot的调用添加到与__getitem__的每次有趣互动中 - 例如__iter__Snit,以及您可以使用的任何其他内容它


实际上,想一想这一点,如果每个Snot只有SnitRefimport 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 描述符 - 这里是代码,对原始内容进行了一些更改:

{{1}}

并且在运行时:

{{1}}

答案 1 :(得分:1)

好的,所以你的Snot变量Snit可变。

class Snot(object):

    def __init__(self, *snits):
        self.snits = [weakref.ref(s) for s in snits]

    def __eq__(self, other):
        if not isinstance(other, self.__class__) and other is not None:
            return NotImplemented
        # are all my snits still valid
        valid = all(s() for s in self.snits)
        if other is None:
            return not valid  # if valid is True, we are not equal to None
        else:
            # whatever it takes to see if this snot is the same as the other snot

实际上让类实例消失会花费更多的工作(例如在类上使用dict来跟踪它们,然后其他数据结构只会使用弱引用 - 但这可能会变得丑陋快速),所以下一个最好的事情就是当它None的任何一个消失时它变得等于Snit


我发现snitssnots都是list s - 订单重要吗?如果顺序不重要,您可以使用set代替,然后就可以有一个高性能的解决方案,其中实际上从数据结构中删除了死snot - 但这会增加复杂性:每个Snot都必须跟踪它所处的数据结构,并且每个Snit都必须保留一个Snot所在的列表,而魔法会有住在__del__,可能导致其他问题......