Automatically delete an instance of a class when one of its attributes becomes dead

Customization

Let's say I have Snit

:

class Snit(): pass

      

And a Snot

that contains weak references up to, say, four 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)

      

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 list

from Snit

(a snit_list

kind of):

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

      

Now I am creating a list Snot

using Snit

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

      

Problem

Oops! I don't need it anymore snit_list[3]

, so I'll go ahead and remove it:

snit_list.pop(3)

      

But now I have Snot

where it is with the dead Snit

:

snot_list[0].s4 # <weakref at 0x00BlahBlah; dead>

      

It doesn't hold up! A Snot

with the dead Snit

is obviously complete nonsense.

So I would really like to see any references Snot

to at least return as None

after destroying one or more of it Snit

. But ideally it would be even better to be Snot

automatically removed from the list snot_list

( len(snot_list)

reduced by the number of Snot

s removed ).

What's a good way to do this?

Clarification:

A Snot

is an object that should only exist when a valid set exists Snit

("valid" means it has the same number of defined ones Snit

that have been initialized), with the following behavior:

  • If any of the Snit

    in Snot

    goes away (when strong links don't stay), Snot

    should go away too (that's why I set s1

    , s2

    etc. to be weak links). Note that a Snot

    could have been initialized with 4, 3, 2, or 1 Snit

    . The number Snit

    does not matter, which is important for death Snit

    .
  • If any Snot

    containing a link to Snit

    go away remains Snit

    .
  • OPTIONAL: upon deletion, Snot

    the data structure containing the reference to the object Snot

    is also updated ( Snot

    gets pop

    ped)
  • OPTIONAL: When ALL of Snots

    this odd reference Snit

    disappears, the value Snit

    also disappears, and any data structures containing Snit

    are updated as in # 3 ( Snit

    gets pop

    PED).

So, a perfect solution would allow me to set up things like this so 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 complicated, I would be fine:

assert snots[12] == None
assert snots[45] == None

      

I can change the situation a little. For example, if it simplifies the design, I think it would be a good idea to remove the weak references to Snit

s, or perhaps move them instead of a list Snit

instead of being members of the Snot

weak references (although I don't see how these changes would improve the situation).

I also looked into subclassing Snot

- ClearSnot

with 1 Snit

, YellowSnot

with 2 Snit

s, GreenSnot

with 3

Snit`, etc. I'm not sure if this will make things easier or more complex.

+3


source to share


2 answers


Nothing is truly automatic. You need to either execute a function that you manually run to check for dead Snit

s, or have a function that is a part Snot

that gets called whenever something interesting happens Snot

to check, and remove, dead Snit

s.

For example:

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

      

The interesting part is adding that the call _remove_dead_snits

for every interesting interaction with a Snot

- for example __getitem__

, __iter__

and whatever you can do with it.




Actually, after thinking about it a bit, if you only have four possible Snit

for each Snot

, you can use a descriptor SnitRef

- here's the code with some changes to your original:

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)

      

and at startup:

[Snit(0), Snit(1), Snit(2), Snit(3)]
Snit(2)
[Snit(0), Snit(1), Snit(3)]
None

      

+2


source


Ok, so you have Snot

with a variable number of 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

      

In fact, if an instance of a class disappears, it will do more work (like having dict

in the class to keep track of all of them, and then other data structures will only use weak references, but that can get ugly quickly), so the next best thing will equal it None

when any of her is Snit

gone.




I see that snits

and snots

are both list

- if the order of importance? If order is not important, you can use set

instead, and then it would be possible to have an execution solution in which the dead is Snot

actually removed from the data structure, but that would add complexity: everyone Snot

would have to keep track of which data line they were in. and each Snit

would have to keep the list Snot

they were on, and the magic would have to live in __del__

, which could lead to other problems ...

+1


source







All Articles