Topics

Destructors - Destroying the Object in Python

Welcome to another tutorial, here you learn about Destructors in Python. In the previous tutorial, you learned about constructors as they are used to create and initialize an object, but, a destructor is used to destroy the object and perform the final clean-up.

However, we have a garbage collector in Python, to clean up the memory. Surprisingly, it is not only the memory that needs cleaning up, when an object is destroyed or dereferenced, there are other sources such as closing open files, cleaning the buffer or cache, closing database connections, and so on. Therefore, the word-final clean-up does not refer to cleaning up the memory resources only.

You already learned about how an object is created in one of the previous tutorials in this tutorial series, by using the '__new__' method and initializing using the ‘__init__’ method. In this tutorial, you will learn how to destroy the object.

Just as explained in the previous tutorial, the ‘__init__’ method is not necessarily the constructor method because it is just responsible to initialize the object variables and not creating the object, which is done by the __new__ method. However, the concept of destructors is also a little blur in python programmers, although commonly the_del__ method is considered the destructor method. 

 

Using the __del__ method

In the example below, we have the class Example, in which, we have used the __init__ method to initialize our object, after which 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;

Output:

Object created
Object destroyed

 

An important point to know

  • A Destructor is a counter-part of a Constructor, that is; function __del__ is the counter-part of function __new__. Since __new__ is the function that creates the object.
  • The __del__ method is called for any object when the reference count for that object becomes zero.
  • When reference counting is performed, then, it is not necessary for an object __del__ method will be called if it is out of scope. As such the destructor method will only be called when the reference count becomes zero.

 

Corner Cases: When the Destructor doesn't behave well

Just as discussed in the above sections, using __del__ is not a full-proof solution to perform the final clean-up for an object which is no longer required.

Below are two situations where the __del__ function behaves absurdly.

 

1. Circular Referencing

This type of referencing refers to a situation in which two objects refer to one another. in this case, both of these objects go out of reference, and at this point, is confused to destroy which object first, and to avoid any error, it does not destroy any of the objects.

Check out this example:

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)

Output:

Foo 12 born 
Bar 12 born

 

2. Exception in __init__ method

In OOP, a destructor can only be called in situations where an object is successfully created. This is so, because, if any exception occurs in the constructor then the constructor itself destroys the object.

However, in python, if any exception occurs in the method __init__while initializing the object, the method __del__ gets called.

However, whether the object was never initialized correctly, the method __del__  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

Output:

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