Destructors in Python
Destructors are special methods in Python used when an object is about to be destroyed or deallocated from memory. It’s usually used for cleanup tasks, such as file closing, memory release, and other resources cleaning.
What is a Destructor?
A destructor is a method defined in a class using the _del_() magic method. A destructor is called automatically when an object goes out of scope or its reference count becomes zero, meaning it will be garbage collected.
How to Define a Destructor?
The destructor is defined as:
class ClassName:
def __del__(self):
# Cleanup code
print("Destructor called, object is being deleted")
Important Points about Destructors:
1. Automatic Call:
- The
_del_()method automatically gets called once the object has been garbage collected.
2. Garbage Collection:
- The garbage collector of Python manages the memory. The object is ready for garbage collection if it is not referenced anymore and hence its destructor gets called.
3. Not Always Immediate:
- It is not a guarantee that the destructor is going to get called immediately after the object becomes unreachable; this depends upon the garbage collection process.
4. Circular References:
- If circular references exist (for example, two objects reference each other), the garbage collector may not immediately collect the objects.
Example 1: A Simple Destructor
Here, we create a class with a destructor and demonstrate how it is called when the object is deleted.
class Example:
def __init__(self, name):
self.name = name
print(f"Object {self.name} created")
def __del__(self):
print(f"Object {self.name} is being destroyed")
# Creating and deleting an object
obj = Example("A") # Object is created
del obj # Explicitly delete the object
Output:
Object A created
Object A is being destroyed
Example 2: Destructor Automatically Invoked
When a function creates an object, the destructor is called automatically when the function ends and the object goes out of scope.
def create_object():
obj = Example("B")
print("Exiting function")
create_object()
print("Program continues...")
Output:
Object B created
Exiting function
Object B is being destroyed
Program continues...
Example 3: Destructor and Resource Management
Destructors are commonly used to manage resources, such as closing files.
class FileHandler:
def __init__(self, file_name):
self.file = open(file_name, 'w')
print("File opened")
def __del__(self):
self.file.close()
print("File closed")
# Creating an object to manage a file
handler = FileHandler("example.txt")
handler = None # Replace the object, triggering the destructor
Output:
File opened
File closed
Example 4: Circular References and Destructors
If two objects reference each other, the garbage collector might not call their destructors immediately because of circular references.
class A:
def __init__(self):
print("Object A created")
def __del__(self):
print("Object A destroyed")
class B:
def __init__(self):
print("Object B created")
def __del__(self):
print("Object B destroyed")
# Creating objects with circular references
a = A()
b = B()
a.other = b
b.other = a
# Deleting references
del a
del b
Output:
Object A created
Object B created
The objects are not destroyed immediately because of the circular reference. Python’s garbage collector will eventually resolve this, but there may be a delay.
Example 5: Destructor vs. Constructor
To understand the lifecycle of an object, here’s an example with both a constructor (__init__) and a destructor (__del__).
class Demo:
def __init__(self):
print("Constructor is called")
def __del__(self):
print("Destructor is called")
# Create and delete an object
obj = Demo() # Constructor is called
del obj # Destructor is called
Output:
Constructor is called
Destructor is called
Best Practices
1. Avoid Complex Logic in Destructors:
- Avoid print statements or raising exceptions inside the destructors since it can lead to quite unpredictable behavior, specially if python is exiting.
2. Use Context Managers:
- Context managers are more suitable for handling resources such as files or database connections, rather than destructors.
with open("file.txt", "w") as file:
file.write("Hello, World!")
# File is automatically closed here.
3. Be Cautious with Circular References:
- Circular references can prevent destructors from being called. Use the
weakrefmodule to avoid such issues if necessary.
Summary of Destructor Behavior
| Scenario | When Destructor is Called |
|---|---|
Object explicitly deleted (del obj) | Immediately when del is called |
| Object goes out of scope | When no references to the object exist |
| Circular references exist | May delay destruction due to garbage collection |