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?
source to share
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
source to share
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, dodel 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/
source to share