As you may know I really like reading books. Here you can find the ones I read in 2024 and 2025 (and in a few months the recap for 2026, actually I should start preparing it).

While I was reorganizing the books in my library I stumbled on the black cover of The Road by Cormac McCarthy with the bookmark of the indie itinerant shop of the second hand book from which I bought it. I opened it and find the following paragraph underlined in pencil.

(editor’s note: this is a literary device to introduce the piece, that it was actually half-baked in summer 2025 right after reading the book but I did not manage to turn into a post up to now)

The fraility of everything revealed at last.

Old and troubling issues resolved into nothingness and night.

The last instance of a thing takes the class with it.

Turns out the light and is gone.

Look around you.

Ever is a long time.

But the boy knew what he knew.

That ever is no time at all.

The literary mind would see a poem, the coder mind will laser focus on a single sentence: The last instance of a thing takes the class with it.

The moment after re-reading this I thought: “In Python that’s not the standard behavior, but is it possible to build a class that satisfy this requirement?”

Or with a little paraphrasing, could we build a class than when its last instance is deleted also the class is gone?

Since animals are used often when explaining concept like inheritance and polymorphism in object oriented programming, I decided for these examples to use the Dodo that has also the unpleasant characteristic of being extinct.

If you want to run the scripts yourself I made a repo (clicking on the numbers should get you at the specific commits) so you don’t have to copy and paste.

Soft Delete

1

dodo.py
class Dodo:
    instance_count = 0
    instanced_once = False

    def __init__(self, name):
        Dodo.instanced_once = True
        Dodo.instance_count += 1
        self.name = name

    def __del__(self):
            Dodo.instance_count -= 1
            if Dodo.instance_count == 0 and Dodo.instanced_once:
                Dodo.delete()

    @classmethod
    def delete(cls):
        if cls.__name__ in globals():
            print(f"Deleting class {cls.__name__}")
            del globals()[cls.__name__]

This class keeps the count of its instances (of course within a script run), increasing by one for every new dodo and decreasing by one every time one is deleted via _del_. If the count reaches zero delete classmethod is called and the class deleted directly by the global variables.

If we run

main.py
from dodo import Dodo

huey = Dodo("Huey")
dewey = Dodo("Dewey")

del huey
del dewey

louie = Dodo("Louie")

we get

Deleting class Dodo
Traceback (most recent call last):
  File "/last-instance-class-main/main.py", line 11, in <module>
    louie = Dodo("Louie")
  File "/last-instance-class-main/dodo.py", line 6, in __init__
    Dodo.instanced_once = True
NameError: name 'Dodo' is not defined
Exception ignored in: <function Dodo.__del__ at 0x0000019758DB251>
Traceback (most recent call last):
  File "/last-instance-class-main/dodo.py", line 11, in __del__
    Dodo.instance_count -= 1
NameError: name 'Dodo' is not defined

After deletion of Huey and Dewey, Louie cannot be actually created and we get NameError: name 'Dodo' is not defined right after the message Deleting class Dodo.

2

We could try to import Dodo again, in this way Louie should have a chance

main.py
from dodo import Dodo

huey = Dodo("Huey")
dewey = Dodo("Dewey")

del huey
del dewey

from dodo import Dodo

louie = Dodo("Louie")

Alas, running it we get

Deleting class Dodo
Traceback (most recent call last):
  File "/last-instance-class-main/main.py", line 9, in <module>
    from dodo import Dodo
ImportError: cannot import name 'Dodo' from 'dodo' ("/last-instance-class-main/dodo.py")

I actually did not expect that! Dodo is still defined in dodo.py. Why can’t we import it anymore?

That’s due to Python’s module caching system.

3

Using importlib and sys we could force the import and create Louie:

main.py
import importlib
import sys

from dodo import Dodo

huey = Dodo("Huey")
dewey = Dodo("Dewey")

del huey
del dewey

importlib.reload(sys.modules['dodo'])

from dodo import Dodo

louie = Dodo("Louie")

4

Or we can simply run another script that imports Dodo since dodo.py is still there:

instantiate_louie.py
from dodo import Dodo

louie = Dodo('Louie')

print('Louie lives')

Actually right after Louie lives we get again Deleting class Dodo. That’s is due to the garbage collector that gets rid of Louie and the class Dodo before exiting.

Hard Delete

With Dodo current implementation dodos don’t get really extinct. How could we modify it so that the last instance of a thing takes the class with it?

5

What about deleting the module dodo.py so that Dodo cannot be imported anymore?

dodo.py
import inspect
import os

class Dodo:
    instance_count = 0
    instanced_once = False

    def __init__(self, name):
        Dodo.instanced_once = True
        Dodo.instance_count += 1
        self.name = name

    def __del__(self):
            Dodo.instance_count -= 1
            if Dodo.instance_count == 0 and Dodo.instanced_once:
                Dodo.delete()

    @classmethod
    def delete(cls):
        if cls.__name__ in globals():
            print(f"Deleting class {cls.__name__}")
            del globals()[cls.__name__]
            filepath = inspect.getfile(cls)
            os.remove(filepath)

Running again main.py you see that it works!

6

If you are a fan of Jurassic Park you could still recover Dodo from __pycache__

main.py
import glob
import importlib.util
import sys

from dodo import Dodo

huey = Dodo("Huey")
dewey = Dodo("Dewey")

del huey
del dewey

pyc_path = glob.glob("__pycache__/dodo*.pyc")[0]
spec = importlib.util.spec_from_file_location("dodo", pyc_path)
dodo_module = importlib.util.module_from_spec(spec)
sys.modules["dodo"] = dodo_module
spec.loader.exec_module(dodo_module)

from dodo import Dodo

louie = Dodo("Louie")

print("Louie lives")

7

For preventing this recover we can delete the file in the pycache together with dodo.py

main.py
import importlib
import inspect
import os


class Dodo:
    instance_count = 0
    instanced_once = False

    def __init__(self, name):
        Dodo.instanced_once = True
        Dodo.instance_count += 1
        self.name = name

    def __del__(self):
            Dodo.instance_count -= 1
            if Dodo.instance_count == 0 and Dodo.instanced_once:
                Dodo.delete()

    @classmethod
    def delete(cls):
        if cls.__name__ in globals():
            print(f"Deleting class {cls.__name__}")
            del globals()[cls.__name__]

            filepath = inspect.getfile(cls)
            os.remove(filepath)

            pyc_filepath = importlib.util.cache_from_source(filepath)

            if os.path.exists(pyc_filepath):
                os.remove(pyc_filepath)

Probably, you could force another way to create the dodos even after dodo.py is gone but that would require some dark magic.

Conclusions

Here a few things that I learned from vibecoding a Python class such that the last instance of a thing takes the class with it:

  • Python uses a caching system for modules
  • It is possible to recover from pycache code contained in deleted .py files
  • I should reorder more often the books in my library