Why doesn't del <object> delete it?

Viewed 97

The code below creates a "tee" object that tees stdout to a file as well as the terminal.

If do del t as below when I'm done tee-ing, the object doesn't get deleted and the __del__() member doesn't get called (so the tee-ing continues):

t = tee("foo.txt")
print("bar")
del t

But if I call __del__() directly, things work fine:

t = tee("foo.txt")
print("bar")
t.__del__()

Why doesn't the del work? What's the right way to do this?

class tee():
    def __init__(self, filepath):
        self.old_stdout = sys.stdout
        self.old_stderr = sys.stderr
        self.name = filepath

        sys.stdout = self
        sys.stderr = self

    def write(self, text):
        self.old_stdout.write(text)
        with open(self.name, 'a',  encoding="utf-8") as f:
           f.write(text)

    def flush(self):
        pass

    def __del__(self):
        sys.stdout = self.old_stdout
        sys.stdout = self.old_stderr
2 Answers

Note del x doesn’t directly call x.__del__() — the former decrements the reference count for x by one, and the latter is only called when x’s reference count reaches zero.
Taken from the data model section of the Python 3 documentation.

You've referenced the class inside the constructor:

sys.stdout = self
sys.stderr = self

The reference will remain and as a result the object will stay “alive”.

What you’re actually looking for is the with-statement that is used for a specific context. You open the file, do something with it and close it again.

with tree(“foo.txt) as t:
    t.write(“bar”)

This will call the exit method in the end.

class tee():
    def __init__(self, filepath):
        self.old_stdout = sys.stdout
        self.old_stderr = sys.stderr
        self.name = filepath

        sys.stdout = self
        sys.stderr = self

    def write(self, text):
        self.old_stdout.write(text)
        with open(self.name, 'a',  encoding="utf-8") as f:
           f.write(text)

    def flush(self):
        pass

    def __exit__(self):
        sys.stdout = self.old_stdout
        sys.stdout = self.old_stderr

Actually your class is doing the same thing with the file handler in the write method.

For the delete method: As the others already mentioned, the delete statement will just decrement the reference counter. Once the number of references has reached zero, the object will be garbage collected and the delete method will be invoked. But this won’t happen in your case since the object is still referenced by the standard output.

Related