[Python-3000] Removing __del__

Nick Coghlan ncoghlan at gmail.com
Tue Sep 26 17:41:58 CEST 2006

Giovanni Bajo wrote:
> It would require some effort to make weakref finalizers *barely* as usable
> as __del__, and will absolutely not solve the problem per-se: the user will
> still have to pay attention and understand the hoops (different kind of
> hoops, but still hoops). So, why do we not spend this same time trying to
> *fix* __del__ instead? If somebody comes up with a sane way to define the
> semantic for a new finalizer method (like the __close__ proposal), which can
> be invoked *even* in the case of cycles, would you still prefer to go the
> weakref way?

Yes. I believe any replacement for __del__ should be syntactic sugar for some 
form of weak reference callback. At the moment, we have two finalization 
methods (__del__ and weakref callbacks). Py3k gives us the opportunity to get 
rid of one of them. Since weakref callbacks are strictly more powerful, then 
__del__ should be the one to go.

Having first made the decision to reduce the number of finalization mechanisms 
to exactly one, I then have no problem with the idea of developing an easy to 
use weakref-based approach to replace the current __del__ (which may or may 
not be a magic method).

> So uhm, am I reading it bad or your implementation (like any other similar
> API I have seen till now) create a cycle *just* by using it?

To use Tim's terminology, the weakref (WR) and the callback (CB) are in a 
cycle with each other, so even after CB is invoked and removes WR from the 
global list of finalizers, the two objects won't go away until the next GC 
collection cycle. The weakly referenced object (WO) itself isn't part of the 
cycle and gets finalized at the first opportunity after its reference count 
goes to zero (as shown in my example - the finalizer ran without having to 
call gc.collect() first).

And don't forget that in non-refcounting implementations like Jython, 
IronPython and some flavours of PyPy, even non-cyclic garbage is collected 
through the GC mechanism at an arbitrary time after the last reference is 
released. If you need prompt finalization (for activities such as closing file 
handles or database connections), that's the whole reason the 'with' statement 
was added in Python 2.5.

All that aside, my example finalizer API only took an hour or two to write, 
compared to the significant amount of effort that has gone into the current 
__del__ implementation. There are actually a number of ways to write weakref 
based finalization that avoid that WR-CB cycle I used, but considering the 
trade-offs between those approaches is a lot more than a two-hour project 
(and, not the least bit incidentally, not an assessment I would really want to 
make on my own ;).

> This finalizer
> API ofhuscates user code by forcing to use a separate _data object to hold
> (part of) the context for apparently no good reason, and make the object
> collectable *only* through the cyclic GC (while __del__ would happily be
> invoked in simple cases when the object goes out of context).

It stores part of the context in a separate object for an *excellent* reason - 
it identifies clearly to the Python interpreter *which* parts of the object 
the finalizer can access. The biggest problem with __del__ is that it 
*doesn't* make that distinction, so the interpreter is forced to assume the 
finalizer might touch any part of the object (including the object itself), 
leading to all of the insanity with self-resurrection and the need to relegate 
things to gc.garbage. With a weakref-based approach, you only end up with two 
possible scenarios:

1. Object gets trashed and finalized
2. Object is kept immortal by a strong reference from the callback in the list 
of finalizers

By avoiding teaching people that care about finalization the important 
distinction between "things the finalizer can get at" and "things the object 
can get at but the finalizer can't", you aren't doing them any favours, 
because maintaining that distinction is the easiest way to avoid creating 
uncollectable cycles (i.e. by making sure the finalizer can't get at the other 
objects that might reference back to the current one).


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia

More information about the Python-3000 mailing list