Python: deleting a self-reference object

I want to ask how to delete a self-reported object in Python.

Think of a class, which is a simple example to know when it is created and when it will be deleted:

#!/usr/bin/python
class TTest:
  def __init__(self):
    self.sub_func= None
    print 'Created',self
  def __del__(self):
    self.sub_func= None
    print 'Deleted',self
  def Print(self):
    print 'Print',self

      

This class has a variable self.sub_func to which we are going to assign a function. I want to assign a function using a TTest instance for self.sub_func. See next example:

def SubFunc1(t):
  t.Print()
def DefineObj1():
  t= TTest()
  t.sub_func= lambda: SubFunc1(t)
  return t

t= DefineObj1()
t.sub_func()
del t

      

Result:

Created <__main__.TTest instance at 0x7ffbabceee60>
Print <__main__.TTest instance at 0x7ffbabceee60>

      

that is , although we did "del t", t was not deleted .

I guess the reason is that t.sub_func is a self-regulating object, so the watchdog counter t does not go to zero at "del t", so t is not garbage collected.

To solve this problem I need to insert

t.sub_func= None

      

before "del t"; at this time the output is:

Created <__main__.TTest instance at 0x7fab9ece2e60>
Print <__main__.TTest instance at 0x7fab9ece2e60>
Deleted <__main__.TTest instance at 0x7fab9ece2e60>

      

But this is strange. t.sub_func is part of t, so I don't want to worry about clearing t.sub_func when removing t .

Could you tell me if you know a good solution?

+3


source to share


2 answers


How do you ensure that an object in the reference loop is deleted when it is no longer available? The simplest solution is not to define a method __del__

. Very few, if any, classes need a method __del__

. Python makes no guarantees as to when or even if a method will be called __del__

.

There are several ways to fix this problem.

  • Use a function, not a lambda containing and testing a weak reference. An explicit check is required that the object is still alive each time the function is called.
  • Create a unique class for each object so that we can store the function in the class and not as a monkey patched function. This can lead to heavy memory.
  • Define a property that knows how to get a given function and turn it into a method. My personal favorite as it closely approximates how bound methods are created from unbound class methods.

Using weak links

import weakref

class TTest:
    def __init__(self):
        self.func = None
        print 'Created', self
    def __del__(self):
        print 'Deleted', self
    def print_self(self):
        print 'Print',self

def print_func(t):
    t.print_self()

def create_ttest():
    t = TTest()
    weak_t = weakref.ref(t)
    def func():
        t1 = weak_t()
        if t1 is None:
            raise TypeError("TTest object no longer exists")
        print_func(t1)
    t.func = func
    return t

if __name__ == "__main__":
    t = create_ttest()
    t.func()
    del t

      



Creating a unique class

class TTest:
    def __init__(self):
        print 'Created', self
    def __del__(self):
        print 'Deleted', self
    def print_self(self):
        print 'Print',self

def print_func(t):
    t.print_self()

def create_ttest():
    class SubTTest(TTest):
        def func(self):
            print_func(self)
    SubTTest.func1 = print_func 
    # The above also works. First argument is instantiated as the object the 
    # function was called on.
    return SubTTest()

if __name__ == "__main__":
    t = create_ttest()
    t.func()
    t.func1()
    del t

      

Using properties

import types

class TTest:
    def __init__(self, func):
        self._func = func
        print 'Created', self
    def __del__(self):
        print 'Deleted', self
    def print_self(self):
        print 'Print',self
    @property
    def func(self):
        return types.MethodType(self._func, self)

def print_func(t):
    t.print_self()

def create_ttest():
    def func(self):
        print_func(self)
    t = TTest(func)
    return t

if __name__ == "__main__":
    t = create_ttest()
    t.func()
    del t

      

+2


source


From CPython official docs :

Objects that have methods __del__()

and are part of the reference loop cause the entire reference loop to be useless, including objects not necessarily in the loop, but only accessible from within. Python does not collect such loops automatically because, in general, Python cannot guess the safe order in which methods are run __del__()

. If you know the safe ordering, you can force fix the problem by looking at the garbage list and explicitly breaking the loops because of your objects in the list. Note that these objects are kept alive even if they are in the garbage list, so they should be removed from the garbage too. For example, after interrupting the loop, do del gc.garbage[:]

to delete the list. It is generally best to avoid this problem by not creating loops containing objects with methods__del__()

and trash can be checked in this case to make sure no such loops are generated.



See also: http://engineering.hearsaysocial.com/2013/06/16/circular-references-in-python/

+2


source







All Articles