Python Destructors Tutorial – Complete Guide

Welcome to an exciting journey through Python destructors! Today, we will delve into a fantastic feature of Python that empowers us to control how objects discard their unneeded resources when they are no longer in use. Whether you are a beginner or a seasoned coder, rest assured, you’ll find this tutorial engaging and insightful.

Why Learn Python Destructors?

Python, as a high-level programming language, is renowned for its simplicity and dynamic semantics. However, it also carries the powerful tool called “destructor”, often underestimated by many developers. Understanding and effectively using destructors can drastically improve your programming skills and take you to a new level of coding proficiency.

In Python, the destructor is a special method that runs when an object is about to be destroyed. It cleans up resources or performs other necessary tasks before the object is discarded. This method is often represented as “__del__” in Python’s classes.

Destructors in Python prove useful in scenarios when an object uses resources that aren’t managed by Python’s memory manager. For instance, if an object opens a file, you might want to make sure that this file gets closed when you’re done with the object. The destructor allows you to make sure that such “cleanup” operations happen automatically.

They simplify memory management by automatically reclaiming resources. With destructors, we can write more efficient, cleaner, and safer code. That’s why understanding destructors is vital for every Python programmer out there.

Let’s get hands-on now and start creating our first destructor!

CTA Small Image
FREE COURSES AT ZENVA
LEARN GAME DEVELOPMENT, PYTHON AND MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Creating a Simple Destructor in Python

For our first example, let’s create a basic class with a destructor. When the object of this class is about to be destroyed, Python will automatically invoke our destructor.

class Gadget: 
    def __init__(self): 
        print("Gadget created") 

    def __del__(self): 
        print("Destructor called, Gadget deleted.")
         
device = Gadget() 
del device

Here, when you run the code, you’ll first see the “Gadget created” message printed, creating the Gadget object. After that, you’ll see “Destructor called, Gadget deleted.” This message is printed because we explicitly called the del method and destroyed our device.

Destructor in a Class with Inheritance

Now, let’s move to slightly more complex code, a class with inheritance. In the following example, we create an abstract class and then define a destructor in one of its subclasses.

class Vehicle:
    def __init__(self): 
        print("Vehicle created") 

class Car(Vehicle):
    def __init__(self): 
        Vehicle.__init__(self)
        print("Car created")

    def __del__(self): 
        print("Destructor called, Car deleted.")

my_car = Car() 
del my_car

Here, by creating an object of the Car class, we invoke the constructor of both the Vehicle and Car classes. The output will be “Vehicle created” and “Car created”. Once you call del my_car, the destructor in the Car class will run, and you’ll see “Destructor called, Car deleted”.

When Python Doesn’t Immediately Call Destructor

In most cases, Python will call the destructor as soon as the object is no longer necessary. However, in some cases, it may not immediately call the destructor. Here is an illustration:

class Gadget: 
    def __init__(self): 
        print("Gadget created") 

    def __del__(self): 
        print("Destructor called, Gadget deleted.") 

def create_and_del_device(): 
    print("Creating object..") 
    device = Gadget() 
    print("Deleting object..") 
    del device 

create_and_del_device()

By running this code, it might seem at first that Python doesn’t call the destructor straight after the del keyword. But fear not, it is simply because the order of execution can sometimes be a bit unpredictable when dealing with destructors.

Using a Destructor to Clean Up Resources

Let’s use a destructor in a more practical example, where the object opens a file, and we want to make sure that this file gets closed when we’re done with the object.

class ReadFile:
    def __init__(self, filename): 
        self.file_obj = open(filename, 'r')

    def __del__(self):
        self.file_obj.close()
        print("Destructor called, file closed.")

reader = ReadFile('sample.txt')
del reader

This script will open the ‘sample.txt’ file and then our destructor will ensure that the file gets closed safely when we’re done with the object.

Handling Exception in Destructors

At times, we might need to handle exceptions within the destructor. Let’s observe how this is done:

class ErrorHandling:
    def __init__(self, value): 
        self.value = value

    def __del__(self):
        try:
           raise self.value
        except Exception as e:
           print("Destructor caught exception:", e)

handler = ErrorHandling(10/0)
del handler

In this example, we purposely create a division by zero error in the destructor. Consequently, the destructor catches it and handles the exception, outputting “Destructor caught exception: division by zero”.

Using a Destructor to Release External Resources

Python destructors can be beneficial in managing and releasing external resources, such as network or database connections. Below is an example of how a destructor might be used to close a database connection:

class DatabaseConnection:
    def __init__(self, db_name): 
        self.db = self.connect_to_db(db_name)
        print(f"Connected to database: {db_name}")

    def __del__(self): 
        self.db.close()
        print("Destructor called, database connection closed.")

    def connect_to_db(self, db_name):
        # Assume this method establishes a connection to the given database
        pass

db_conn = DatabaseConnection("my_database")
del db_conn

Note: This is a conceptual example, and you would replace the content of the connect_to_db method with actual database connection code.

Calling Destructors from a Dictionary of Objects

Python’s destructors are also handy when dealing with a collection of objects, like in a dictionary. Let’s see how it works:

class Gadget:
    def __init__(self, id):
        self.id = id
        print(f"Gadget {self.id} created")

    def __del__(self):
        print(f"Destructor called, Gadget {self.id} deleted.")

collection = {}
for i in range(5):
    collection[i] = Gadget(i)

del collection

In this example, we create a dictionary that holds Gadget objects. When the entire dictionary is deleted, Python automatically calls the destructor for each object in the dictionary.

Destructors in Multithreaded Scenarios

When it comes to multithreading, it is important to understand that the destructor can be called from any thread, not just the one that created the object. Here is an example:

import threading
import time

class ThreadSafeClass:
    def __init__(self):
        print(f"Object created in {threading.current_thread().name}")
    
    def __del__(self):
        print(f"Destructor called from {threading.current_thread().name}")
        
def create_object():
    obj = ThreadSafeClass()
    time.sleep(1)  # simulating a long running operation

thread = threading.Thread(target=create_object)
thread.start()
thread.join()

In this case, Python might decide to reclaim the local variables of the ‘create_object’ function from a different thread than the one that created the object. It’s important to keep this in mind when working with resources that are not thread-safe.

Where to Go Next

Hopefully, you now have a solid understanding of Python destructors and how to apply them in resource management. We trust that this tutorial has whetted your appetite for mastering more complex Python concepts.

Python is a diverse language, and knowing where to head next can sometimes feel overwhelming. That’s why we, at Zenva, created our comprehensive Python Mini-Degree. This resource is a collection of courses that covers a wide range of topics, from coding basics and algorithms to object-oriented programming, game development, and application development.

What’s fantastic about these courses is that they aren’t just standalone lectures. We believe in learning by doing, so you will be creating your own real-world projects, applications, and games while you learn. This approach gives you hands-on experience and a better understanding of how Python works. Why Choose Zenva For Learning Python?

  • Our courses span from beginners to professionals, meaning everyone has something to learn with us.
  • You can earn certifications upon completion, giving your resume a significant boost.
  • We offer interactive lessons, coding challenges, and quizzes to ensure a comprehensive understanding of the topics covered.
  • Our courses are flexible, allowing you to study at your own pace.
  • Every student gets access to mentors who are experienced in programming and teaching.

Whether you’re a beginner starting from scratch or an experienced coder looking to expand your knowledge, the Python courses offered by Zenva have got you covered.

Conclusion

With an insight into Python destructors and a hands-on approach, we can now appreciate the crucial role they play in effective and efficient programming. The power and flexibility that Python provides, combined with the right knowledge and skills, can drastically elevate your coding competence. Embrace Python destructors as an integral part of your coding skillset and continue advancing in your Python journey.

As you take your next steps towards mastering Python, consider Zenva’s resourceful and comprehensive Python Mini-Degree. Immerse yourself in our flexible, interactive, and engaging courses that will empower you to unlock a world of possibilities. Let’s dive deeper into the world of Python together!

Did you come across any errors in this tutorial? Please let us know by completing this form and we’ll look into it!

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.