On Oct 7, 11:28 pm, Steven D'Aprano <[EMAIL PROTECTED]> wrote: > On Sun, 07 Oct 2007 12:55:29 -0700, Alex Martelli wrote: > >> What should I do when my objects need to perform some special > >> processing when they are freed, if I shouldn't use __del__? > > > The solid, reliable way is: > > > from __future__ import with_statement > > > and use module contextlib from the Python standard library (or handcode > > an __exit__ method, but that's rarely needed), generating these special > > objects that require special processing only in 'with' statements. This > > "resource acquisition is initialization" (RAII) pattern is the RIGHT way > > to ensure timely finalization (particularly but not exclusively in > > garbage-collected languages, and particularly but not exclusively to > > ease portability to different garbage collection strategies -- e.g., > > among CPython and future versions of IronPython and/or Jython that will > > support the with statement). > > Hmmm... I'm not sure I understand how a with statement is meant to > replace class.__del__ in practice. It seems to me that the two things > have different uses. with is useful when you create an object, do > something with it, dispose of it in an orderly fashion, and then > continue. How does that help when you create an unknown number of long- > lasting objects where you can't predict how and when they will be > disposed of? > > Here's a minimal example. I have a module that returns parrot objects: > > # Parrot module > class Parrot(object): > def __del__(self): > print "I'm now pushing up the daisies" > > # Callers code: > import Parrot > p = Parrot.Parrot() > alist = [Parrot.Parrot() for i in range(1000)] > # do something with p and alist > print p, alist > # instances are deleted in arbitrary order > del p > del alist[3] > del alist[853] > del alist[701] > del alist > > It seems to me that you are saying to re-write the above as something > like this: > > # Parrot module > class Parrot(object): > pass > > class killed(object): # context manager > def __exit__(self, *exc_info): > print "I'm now pushing up the daisies" > > # Callers code: > import Parrot > with killed(Parrot.Parrot()) as p: > # ... er, what do I do with alist??? > # do arbitrary stuff with p and alist > print p, alist > > Now, I don't think the above is feasible. What am I missing?
Rename your __del__ to close and call it explicitely: p.close() alist[3].close() alist[853].close() alist[701].close() > > An alternative that will work in pre-2.5 Python (and, I believe but I'm > > not sure, in Jython and IronPython _today_) is to rely on the weakref > > module of the standard Python library. > > ... > > I'm going to need some time to play around with that, but it seems > reasonably straightforward. But the obvious thing I can see is that > there's an awful lot of boilerplate code that needs to be written for > each and every class that needs a "weak finalizer". > > That is to say, Python "guarantees" that __del__ will always be called > (unless it isn't... "guarantee void on planet Earth") for your objects, > so the developer doesn't need to do anything except write the finalizer. Yep, the problem is that __del__ does NOT guarantee anything. So, it *looks* easy, but it is actually very fragile and in subtle cases can betray you without telling. I, for one, I don't like to have bombs in my code waiting to explode at some unknown moment ... > To do the right thing with weakrefs means that not only does the > developer have to write the finalizer, but he has to write the code to > ensure the finalizer is called. I'm not sure that's a step forward. Yep, we would need a better support for finalizers in the standard library. For the moment, there are various recipes in the Python cookbook. See also this post: http://groups.google.com/group/comp.lang.python/browse_frm/thread/b09e02b7c67c8a3b/1ef0f64bb539cfb0?hl=en&lnk=gst&q=simionato+__del__&rnum=1#1ef0f64bb539cfb0 -- http://mail.python.org/mailman/listinfo/python-list