Automatically delete class instance when one of its attributes becomes dead

后端 未结 2 380
既然无缘
既然无缘 2021-01-20 06:10

Set Up

Say I have a Snit:

class Snit(): pass

And a Snot, which contains weak references to up to, say,

相关标签:
2条回答
  • 2021-01-20 06:40

    Okay, so you have a Snot with a variable amount of Snits.

    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
    

    Actually having the class instance disappear is going to take more work (such as having dict on the class to track them all, and then other data structures would just use weak-references -- but that could get ugly quick), so the next best thing will be having it become equal to None when any of its Snits goes away.


    I see that snits and snots are both lists -- is order important? If order is not important you could use sets instead, and then it would be possible to have a performant solution where the the dead snot is actually removed from the data structure -- but it would add complexity: each Snot would have to keep track of which data struture it was in, and each Snit would have to keep a list of which Snots it was in, and the magic would have to live in __del__ which can lead to other problems...

    0 讨论(0)
  • 2021-01-20 06:46

    Nothing is truly automatic. You'll need to either have a function that you run manually to check for dead Snits, or have a function that is part of Snot that is called whenever anything interesting happens to a Snot to check for, and remove, dead Snits.

    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 fun part is adding that call to _remove_dead_snits for every interesting interaction with a Snot -- such as __getitem__, __iter__, and whatever else you may do with it.


    Actually, thinking a bit more about this, if you only have the four possible Snits per each Snot you could use a SnitRef descriptor -- 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 when run:

    [Snit(0), Snit(1), Snit(2), Snit(3)]
    Snit(2)
    [Snit(0), Snit(1), Snit(3)]
    None
    
    0 讨论(0)
提交回复
热议问题