Why Python's __del__ Can Be Dangerous
Python's __del__ method for cleanup can cause circular reference deadlocks, fail during interpreter shutdown, and silently swallow exceptions. Learn safer alternatives like context managers and weakref.finalize.
Why Python’s __del__ Might Be the Most Dangerous Magic Method You’ll Ever Use
Let me tell you a story. A friend of mine, let's call him Alex, was building a data processing pipeline at a startup. He needed to make sure that every time a certain object was garbage collected, it would cleanly close a database connection. "Easy," he thought, and wrote a __del__ method. Three weeks later, the production database started throwing random connection errors at midnight, and nobody could figure out why.
Turns out, __del__ was the culprit. And if you're using it in Python, you might be heading down the same rabbit hole.
What __del__ Actually Does (And Doesn't Do)
Python's __del__ method gets called when the reference count of an object drops to zero. Sounds straightforward, right? But here's where it gets messy:
class ResourceManager:
def __init__(self):
self.connection = open_database_connection()
def __del__(self):
self.connection.close()
Looks clean. But this code is a ticking time bomb.
The Three Silent Killers
1. The Circular Reference Trap
If two objects reference each other, and both have __del__ methods, Python's garbage collector can't decide which to delete first. Your __del__ might never get called. Not "sometimes" - never.
class Parent:
def __init__(self):
self.child = None
def __del__(self):
print("Parent cleanup")
class Child:
def __init__(self):
self.parent = None
def __del__(self):
print("Child cleanup")
Both objects wait for the other to die first. Classic deadlock.
2. The Module Attribute Disaster
Remember how Python shuts down? Modules get cleaned up too. If your __del__ method tries to access sys.stderr or any global object after it's been set to None, you'll get a cryptic AttributeError - or worse, it'll just fail silently.
I once spent two days debugging why log files weren't being written properly. The __del__ method was trying to write to a logger that had already been garbage collected.
3. The Exception Black Hole
Raise an exception in __del__? Python swallows it. Not logs it, not prints it - swallows it entirely.
def __del__(self):
raise RuntimeError("Something went wrong")
This runs, fails, and nobody knows. Your cleanup might silently break, and you'll never get a stack trace.
What You Should Use Instead
PythonSkillset readers, here's your save: contextlib and the with statement.
from contextlib import contextmanager
@contextmanager
def managed_resource():
connection = open_database_connection()
try:
yield connection
finally:
connection.close()
Then use it:
with managed_resource() as conn:
# do your work
# cleanup happens automatically
This is predictable, testable, and doesn't depend on Python's garbage collection timing.
When __del__ Actually Makes Sense
There are two legitimate use cases:
- C extension wrappers where the C code needs deterministic cleanup
- Temporary file cleanup where you're dealing with file descriptors
But even then, most PythonSkillset guides will tell you to use weakref.finalize instead:
import weakref
class MyClass:
def __init__(self):
self._cleanup = weakref.finalize(self, self._cleanup_resources)
@staticmethod
def _cleanup_resources():
print("Cleanup happens reliably")
weakref.finalize handles circular references gracefully and won't blow up during interpreter shutdown.
The Bottom Line
Here's what I tell every developer I mentor: treat __del__ like you would treat goto in C. It exists, it works sometimes, and you should have a very, very good reason before using it. The Python language designers themselves recommend avoiding it in most cases.
Instead of fighting with garbage collection timing, use context managers. Instead of hoping __del__ runs in the right order, use weakref.finalize. Your future self - and your production database - will thank you.
Next time you think "I'll just use __del__ for cleanup," remember Alex and the midnight database errors. There's always a better way.
Comments
Questions, corrections, and tips stay visible for everyone reading this page.
Join the discussion
No comments yet
Be the first to leave a note — it helps the next reader.