Python

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.

July 2026 5 min read 11 views 0 hearts

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:

  1. C extension wrappers where the C code needs deterministic cleanup
  2. 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.

0 in thread

Join the discussion

Shown next to your comment.

Up to 4,000 characters

No comments yet

Be the first to leave a note — it helps the next reader.