[Python-Dev] Design question: call __del__ only after successful __init__?

Guido van Rossum guido@python.org
Fri, 03 Mar 2000 12:05:00 -0500

OK, so we're down to this one point: if __del__ resurrects the object,
should __del__ be called again later?  Additionally, should
resurrection be made illegal?

I can easily see how __del__ could *accidentally* resurrect the object
as part of its normal cleanup -- e.g. you make a call to some other
routine that helps with the cleanup, passing self as an argument, and
this other routine keeps a helpful cache of the last argument for some
reason.  I don't see how we could forbid this type of resurrection.
(What are you going to do?  You can't raise an exception from
instance_dealloc, since it is called from DECREF.  You can't track
down the reference and replace it with a None easily.)
In this example, the helper routine will eventually delete the object
from its cache, at which point it is truly deleted.  It would be
harmful, not helpful, if __del__ was called again at this point.

Now, it is true that the current docs for __del__ imply that
resurrection is possible.  The intention of that note was to warn
__del__ writers that in the case of accidental resurrection __del__
might be called again.  The intention certainly wasn't to allow or
encourage intentional resurrection.

Would there really be someone out there who uses *intentional*
resurrection?  I severely doubt it.  I've never heard of this.

[Jack just finds a snag]

> The __init__ rule for calling __del__ has me confused. Is this per-class or 
> per-object?
> I.e. what will happen in the following case:
> class Purse:
> 	def __init__(self):
> 		self.balance = WithdrawCashFromBank(1000)
> 	def __del__(self):
> 		PutCashBackOnBank(self.balance)
> 		self.balance = 0
> class LossyPurse(Purse):
> 	def __init__(self):
> 		Purse.__init__(self)
> 		 raise 'kaboo! kaboo!'
> If the new scheme means that the __del__ method of Purse isn't called I think 
> I don't like it. In the current scheme I can always program defensively:
> 	def __del__(self):
> 		try:
> 			b = self.balance
> 			self.balance = 0
> 		except AttributeError:
> 			pass
> 		else:
> 			PutCashBackOnBank(b)
> but in a new scheme with a per-object "__del__ must be called" flag I can't...

Yes, that's a problem.  But there are other ways for the subclass to
break the base class's invariant (e.g. it could override __del__
without calling the base class' __del__).

So I think it's a red herring.  In Python 3000, typechecked classes
may declare invariants that are enforced by the inheritance mechanism;
then we may need to keep track which base class constructors succeeded
and only call corresponding destructors.

--Guido van Rossum (home page: http://www.python.org/~guido/)