python - 当其中一个属性失效时自动删除类实例

标签 python

设置

假设我有一个 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])

还有 Snitlist(可以说是 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>

这受不了了!带有死的 SnitSnot - 显然 - 完全是胡说八道。

所以我真的希望任何对 Snot 的引用至少在它的一个或多个 Snit 已经返回后作为 None 返回被摧毁。但理想情况下,Snot 也从 snot_list 列表中自动删除会更好(len(snot_list) 缩小了删除的 Snot 数量)。

解决这个问题的好方法是什么?

澄清:

Snot 是一个对象,只有当存在一组有效的 Snit 时才应该存在(“有效”意味着它具有相同数量的已定义 Snits 它被初始化),具有以下行为:

  1. 如果 Snot 中的任何一个 Snit 消失了(当没有强引用保留时),Snot 也应该消失(这是为什么我将 s1s2 等设置为弱引用)。请注意,Snot 可能 已使用 4、3、2 或 1 个 Snit 进行了初始化。 Snit 的数量并不重要,重要的是 Snit死亡
  2. 如果任何一个包含对 Snit 的引用的 Snot 消失了,Snit 就会保留下来。
  3. 可选:删除 Snot 时,包含对 Snot 对象的引用的数据结构也会更新(Snot 获取popped)
  4. 可选:当引用某个SnitALL Snots 消失时,Snit 也会消失, 并且任何包含 Snit 的数据结构都会像 #3 中那样更新(Snit 得到 popped)。

所以理想的解决方案将允许我进行设置,以便我可以编写如下代码:

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 个 SnitYellowSnot 有 2 个 SnitGreenSnot 和 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/

相关文章:

Python:带有域检查的线性代数包

python - 如何使用 groupby 计算平均每周支出,周为周一至周日?

Python 3 - 将枚举与十六进制值进行比较

python - 使用 python pandas 和 beautifulSoup 抓取分页网络表

python - PyQt4 全局快捷方式?

python - Pandas: reshape 数据框

python - 如何操作jinja html文件

python - 最长 200 字节字符串 - 数据库验证 max_length

python - 导入模块时发生奇怪的事情

python - python中的webbrowser.open()