8

I have been thinking about how I write classes in Python. More specifically how the constructor is implemented and how the object should be destroyed. I don't want to rely on CPython's reference counting to do object cleanup. This basically tells me I should use with statements to manage my object life times and that I need an explicit close/dispose method (this method could be called from __exit__ if the object is also a context manager).

class Foo(object):
    def __init__(self):
        pass
    def close(self):
        pass

Now, if all my objects behave in this way and all my code uses with statements or explicit calls to close() (or dispose()) I don't realy see the need for me to put any code in __del__. Should we really use __del__ to dispose of our objects?

Arlaharen
  • 3,095
  • 29
  • 26
  • 2
    I don't want to rely on CPython's reference counting to do object cleanup. Why on earth not? What's wrong with object reference counting? – S.Lott Jul 10 '09 at 21:35
  • @S.Lott, reference counting is just not there if you move your code to an environment with more advanced garbage collection, such as Jython or IronPython -- and what if one day CPython (e.g. via Unladen Swallow) gets dragged kicking and screaming into the 21st century, too? I agree with the OP about "not relying" on RC in my reusable, portable libraries (as opposed to one-use, throw-away, scripts and minor modules). – Alex Martelli Jul 11 '09 at 02:51
  • @S.Lott: Alex already answered your question. Another good reason for wanting to not rely on reference counting is the JIT coming out of the PyPy project. The people behind it seems te be optimistic that it will be able to deliver substatial speed ups compared to CPython. – Arlaharen Jul 12 '09 at 09:58

3 Answers3

15

Short answer : No.

Long answer: Using __del__ is tricky, mainly because it's not guaranteed to be called. That means you can't do things there that absolutely has to be done. This in turn means that __del__ basically only can be used for cleanups that would happen sooner or later anyway, like cleaning up resources that would be cleaned up when the process exits, so it doesn't matter if __del__ doesn't get called. Of course, these are also generally the same things Python will do for you. So that kinda makes __del__ useless.

Also, __del__ gets called when Python garbage collects, and you didn't want to wait for Pythons garbage collecting, which means you can't use __del__ anyway.

So, don't use __del__. Use __enter__/__exit__ instead.

FYI: Here is an example of a non-circular situation where the destructor did not get called:

class A(object):
    def __init__(self):
        print('Constructing A')

    def __del__(self):
        print('Destructing A')

class B(object):
    a = A()

OK, so it's a class attribute. Evidently that's a special case. But it just goes to show that making sure __del__ gets called isn't straightforward. I'm pretty sure I've seen more non-circular situations where __del__ isn't called.

Lennart Regebro
  • 167,292
  • 41
  • 224
  • 251
  • I asked a similar question a year ago. So far, no one has been able to name a legitimate scenario in which the `__del__` method would be of any use at all. As far as I'm concerned, the method should never have been in Python in the first place. It's good for nothing and gives people the false impression that they could use it for cleanup. – antred Dec 05 '16 at 13:13
8

Not necessarily. You'll encounter problems when you have cyclic references. Eli Bendersky does a good job of explaining this in his blog post:

ars
  • 120,335
  • 23
  • 147
  • 134
  • 1
    unfortunately the article linked draws a wrong conclusion. `__del__` is not guaranteed to get called. At all. It’s not only for cyclical references. Also, it says **"Cyclic references, however, are often a sign of bad design"** WTF? – nosklo Jan 24 '11 at 18:11
0

If you are sure you will not go into cyclic references, then using __del__ in that way is OK: as soon as the reference count goes to zero, the CPython VM will call that method and destroy the object.

If you plan to use cyclic references - please think it very thoroughly, and check if weak references may help; in many cases, cyclic references are a first symptom of bad design.

If you have no control on the way your object is going to be used, then using __del__ may not be safe.

If you plan to use JPython or IronPython, __del__ is unreliable at all, because final object destruction will happen at garbage collection, and that's something you cannot control.

In sum, in my opinion, __del__ is usually perfectly safe and good; however, in many situation it could be better to make a step back, and try to look at the problem from a different perspective; a good use of try/except and of with contexts may be a more pythonic solution.

rob
  • 36,896
  • 2
  • 55
  • 65