Set Up
Say I have a Snit:
class Snit(): pass
And a Snot, which contains weak references to up to, say, four Snits:
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)
I also have a Snot factory:
def snot_factory(snits,w,x,y,z):
return Snot(snits[w],snits[x],snits[y],snits[z])
And a list of Snits (a snit_list as it were):
snit_list = []
for i in range(12):
snit_list.append(Snit())
Now I make a list of Snots using the Snits in my 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]))
The Problem
Whoops! I don't need snit_list[3] anymore, so I'll go ahead and remove it:
snit_list.pop(3)
But now I have a Snot hanging out there with a dead Snit:
snot_list[0].s4 # <weakref at 0x00BlahBlah; dead>
This cannot stand! A Snot with a dead Snit is - obviously - total nonsense.
So I would really like for any references to the Snot to at least return as None after one or more of its Snits has been destroyed. But ideally, it would be even better for the Snot to be automatically removed from the snot_list list as well (len(snot_list) shrinks by the number of removed Snots).
What's a good way of going about this?
Clarification:
A Snot is an object that should only exist when there is a valid set of Snits ("valid" means it has the same number of defined Snits it was initialized with), with the following behavior:
- If any one
Snitin aSnotgoes away (when no strong references remain), theSnotshould also go away (this is why I have set thes1,s2, etc to be weak references). Note that aSnotcould have been initialized with 4, 3, 2, or 1Snit. The number ofSnits doesn't matter, the death of theSnitis what matters. - If any one
Snotthat contains a reference to aSnitgoes away, theSnitremains. - OPTIONAL: When a
Snotis deleted, the data structure containing the reference to theSnotobject is updated as well (theSnotgetspopped) - OPTIONAL: When ALL the
Snotsthat reference a certainSnitare gone, theSnitgoes away too, and any data structures containing theSnitare updated as in #3 (theSnitgetspopped).
So the ideal solution will allow me to set things up such that I can write code like this:
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
However, if this is too hard, I'd be fine with:
assert snots[12] == None
assert snots[45] == None
I am open to changing things around somewhat. For example, if it makes the design easier, I think it would be fine to remove the weak references to the Snits, or to maybe move them instead to the list of Snits instead of having the Snot members be weak refs (though I don't see how either of these changes would improve things).
I have also considered creating Snot subclasses - ClearSnot with 1 Snit, YellowSnot with 2 Snits, GreenSnot with 3 Snits, etc. I'm uncertain if this would make things easier to maintain, or more difficult.