Normally we will use __init__ as the constructor, but what shall we do when we are done with the object?
the key lies in the __del__ method, when the object's reference counting is down to 0, then the destructor will be called.
Below shows an example object that wraps around the file object.
class SpecialFiles(object): ''' classdocs ''' def __init__(self, file_name): ''' Constructor ''' self.__file = open(file_name, 'w') self.__file.write("**** Start Special File **** \n\n") def write(self, str): self.__file.write(str) def writelines(self, str_list): self.__file.writelines(str_list) def __del__(self): if self.__file is not None: print("__del__ called on", self.__file.name) self.__file.close() else: print("close() is already called") def close(self): if self.__file: self.__file.write('\n\n**** End Special File') self.__file.close() self.__file = None
First we have defined a __del__ method which shall be called when the reference counting is reaching 0, and we are also providing a close() method which we can deterministic destruct an object, and internally it shall call the __del__ ethod. Below shows how we will use it .
import unittest from Classes.Inheritances.Destructors import SpecialFiles class Test(unittest.TestCase): def testDestructors(self): f = SpecialFiles('testfile') f.write('11111\n') f.close() def testDestructors1(self): f = SpecialFiles('testfile')so when you run this test method, you will see when the references are reaching the 0, the __del__ method will be called automatically, but in the testDestructors, we can see the cleanup mehtod close runs before the function returns, but the __del__ method runs after the function is returned.
class Circle: def __init__(self, name, parent): self.name = name self.parent = parent self.child = None if parent: parent.child = self def cleanup(self): self.child = self.parent = None def __del__(self): print("__del__ called on", self.name)Let's make two test cases, one is a test case with circular reference, one is a test case without.
import unittest from Classes.Inheritances.Destructors import Circle class Test(unittest.TestCase): def testMemoryLeakage(self): # you won't see # __del__ called on a = Circle("a", None) b = Circle("b", a) def testToBreakCircularReferences(self): c = Circle('c', None) d = Circle('d', c) d.cleanup() if __name__ == "__main__": #import sys;sys.argv = ['', 'Test.testDestructors'] unittest.main()As you can see, when you are in doubt, you shall always provide a explicit cleanup method, like the cleanup method which showed above, which can help to break the circular reference and more importantly explicitly clean up the resource if necessary.