设置
假设我有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
s 的list
(可以说是snit_list
):
snit_list = []
for i in range(12):
snit_list.append(Snit())
现在,我在snit_list
中使用Snit
s列出了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_list
列表中删除Snot
(len(snot_list)
会缩小删除Snot
的数量)。
的好方法是什么?
澄清:
Snot
是一个对象,仅当存在一组有效的 Snit
s 时才存在("有效"表示它具有与初始化时相同数量的已定义Snit
),具有以下行为:
- 如果
Snot
中的任何一个Snit
消失(当没有强引用时),Snot
也应该消失(这就是为什么我将s1
、s2
等设置为弱引用)。请注意,Snot
可能已初始化为 4、3、2 或 1Snit
。Snit
的数量并不重要,Snit
的死亡才是最重要的。 - 如果包含对
Snit
引用的任何Snot
消失,则Snit
仍然存在。 - 可选:删除
Snot
时,包含对Snot
对象的引用的数据结构也会更新(Snot
pop
) - 可选:当引用某个
Snit
的所有Snots
都消失时,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
s 的弱引用,或者将它们移动到 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
有趣的部分是添加对_remove_dead_snits
的调用,以便与Snot
进行每次有趣的交互 - 例如__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
好的,所以你有一个Snot
,Snit
s的数量是可变的。
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
消失时。
我看到snits
和snots
都是list
- 顺序重要吗? 如果顺序不重要,你可以改用set
s,然后有可能有一个高性能的解决方案,其中死snot
实际上从数据结构中删除 - 但它会增加复杂性:每个Snot
都必须跟踪它所在的数据结构, 每个Snit
都必须保留一份清单,列出它在哪个Snot
,而魔法必须存在于__del__
,这可能会导致其他问题......