[Python-3000] Removing __del__

Jim Jewett jimjjewett at gmail.com
Wed Sep 20 21:04:23 CEST 2006


On 9/20/06, Michael Chermside <mcherm at mcherm.com> wrote:
> Giovanni Bajo writes:
> > I believe my example is a good use case for __del__ with no good
> > enough workaround, ... I still like the __close__ method being proposed.

[Michael asks about this alternative]
...
> def on_del_invoke(obj, func, *args, **kwargs):
...
>      Please note that the callable must not be a bound method
>      of the object being watched, and the object being watched
>      must not be (or be refered to by) one of the arguments
>      or else the object will never be garbage collected."""

By far the most frequently desired callable is self.close

You can work around this with a wrapper, by setting self.f=open(...)
and then passing self.f.close -- but with this API, I'll be wondering
why I can't just register self.f as the object in the first place.

If bound methods did not increment the refcount, this would work, but
I imagine it would break various GUI and event-processing idioms.

A special rebind-this-method-weakly builtin would work, but I'm not
sure that is any simpler than __close__.  (~= __del__ but cycles can
be broken in an arbitrary order)

> Using this, your original code:

> > class Wrapper:
> >     def __init__(self, *args):
> >          self.handle = CAPI.init(*args)

> >     def __del__(self, *args):
> >           CAPI.close(self.handle)

> >     def foo(self):
> >           CAPI.foo(self.handle)

> becomes this code:

>    from deletions import on_del_invoke

>    class Wrapper:
>        def __init__(self, *args):
>            self.handle = CAPI.init(*args)
>            on_del_invoke(self, CAPI.close, self.handle)

>        def foo(self):
>            CAPI.foo(self.handle)

Note that the wrapper (as posted) does nothing except store a pointer
to the CAPI object and then delegate to it.  With a __close__ method,
this class could reduce to (at most)

    class MyCAPI(CAPI):
        __close__ = close

Since the CAPI class could use the __close__ convention directly, the
wrapper could be eliminated entirely.  (In real life, his class might
do more ... but if so, then *these* lines are still boilerplate that
it would be good to remove).

> On the other side of the scales, here are some benefits that
> we gain if we get rid of __del__:

>    * No need to explain about keeping __del__ objects[1] out
>      of reference loops. In exchange, we choose explain
>      about not passing the object being monitored or
>      anything that links to it as arguments to on_del_invoke.

Adding an extra wrapper just to avoid passing self isn't really any
better than adding an extra cleanup object hanging off an attribute to
avoid loops.  So the explanation might be better, but the resulting
code would end up using the same workarounds that are recommended (but
often not used) today.

>     (3) if the programmer violates this rule then the
>      disadvantage is that the objects become immortal -- which
>      is true for ALL __del__ objects in loops today.

But most objects are not in a __del__ loop.  By passing a bound
method, the user makes the object immortal even if it is the only
object that needs cleanup.

>    * Programmers no longer have the ability to allow __del__
>      to resurect the object being finalized. Technically,
>      that's a disadvantage, not an advantage, but I honestly
>      don't think anyone believes it's a good idea to write
>      __del__ methods that resurect the object, so I'm happy
>      to lose that ability.

How do you feel about the __del__ in stdlib subprocess.Popen (about line 615)?

This resurrects itself, in order to finish waiting for the child
process.  If the child isn't done yet, then it will check again the
next time a new Popen is created (or at final closedown).  Without
this ability to reschedule itself, it would have to do a blocking
wait, which might put some odd pressures on concurrency.

(And note that if it needed to revive (not recreate, revive)
subobjects, it would need the full immortal-cycle power of today's
__del__.  It may be valid not to support this case, but it isn't
automatically bad usage.)

-jJ


More information about the Python-3000 mailing list