Destructors - Destroying the Object

Just like a constructor is used to create and initialize an object, a destructor is used to destroy the object and perform the final clean up.

Although in python we do have garbage collector to clean up the memory, but its not just memory which has to be freed when an object is dereferenced or destroyed, it can be a lot of other resources as well, like closing open files, closing database connections, cleaning up the buffer or cache etc. Hence when we say the final clean up, it doesn't only mean cleaning up the memory resources.

In our last tutorial we learned how object is created using __new__ method and initialised using the __init__ method. In this tutorial, we will learn how to destroy the object.

As we specified clearly in the last tutorial, that __init__ method is not necessarily the constructor method as it is only repsonsible to initialise the object variables and not create the object, which is done by __new__ method.

Similarly, the concept of destructors is also a little blur in python, although commonly __del__ method is considered as the destructor method, so lets see how we can use this method to destroy a class's object.


Using the __del__ method

Below we have a simple code for a class Example, where we have used the __init__ method to initialize our object, while we have defined the __del__ method to act as a destructor.

class Example:
	def __init__(self):
		print "Object created"
	
	# destructor
	def __del__(self):
	    print "Object destroyed"

# creating an object
myObj = Example()
# to delete the object explicitly
del myObj

Object created Object destroyed


Few Points to Note

  1. Like Destructor is counter-part of a Constructor, function __del__ is the counter-part of function __new__. Because __new__ is the function which creates the object.
  2. __del__ method is called for any object when the reference count for that object becomes zero.
  3. As reference counting is performed, hence it is not necessary that for an object __del__ method will be called if it goes out of scope. The destructor method will only be called when the reference count becomes zero.


Corner Cases: When Destructor doesn't behave well

As we already mentioned in the start that using __del__ is not a full proof solution to perform the final clean up for an object which is no longer required.

Here we have discussed two such situations where __del__ function behaves absurd.


1. Circular Referencing

Circular referencing refers to a situation where two objects refers to each other. In such a case when both of these objects goes out of reference, python is confused to destroy which object first, and to avoid any error, it doesn't destroy any of them.

Here is an example to demonstrate circular referencing,

class Foo():
    def __init__(self, id, bar):
        self.id = id
        # saving reference of Bar object
        self.friend = bar
        print 'Foo', self.id, 'born'

    def __del__(self):
        print 'Foo', self.id, 'died'


class Bar():
    def __init__(self, id):
        self.id = id
        # saving Foo class object in variable
        # 'friend' of Bar class, and sending
        # reference of Bar object for Foo object
        # initialisation
        self.friend = Foo(id, self)
        print 'Bar', self.id, 'born'

    def __del__(self):
        print 'Bar', self.id, 'died'


b = Bar(12)

Foo 12 born Bar 12 born


2. Exception in __init__ method

In object oriented progamming, destructor is only called in case an object is successfully created, because if the any exception occurs in the constructor then the constructor itself destroys the object.

But in python, if any exception occurs in the __init__ method while initialising the object, in that case too, the method __del__ gets called.

Hence, even though the object was never initialised correctly, the __del__ mehthod will try to empty all the resources and variables and in turn may lead to another exception.

class Example():
	def __init__(self, x):
	    # for x = 0, raise exception
		if x == 0:
			raise Exception()
		self.x = x
		
	def __del__(self):
		print self.x

# creating an object
myObj = Example()
# to delete the object explicitly
del myObj

Exception exceptions.AttributeError: "'Example' object has no attribute 'x'" in <bound method Example.__del__ of <__main__.Example object at 0x02449570>> ignored