Why doesn't my finaliser run here?

Steve D'Aprano steve+python at pearwood.info
Sun Sep 4 21:29:39 EDT 2016


On Sun, 4 Sep 2016 10:37 pm, Ben Finney wrote:

> Steve D'Aprano <steve+python at pearwood.info> writes:
> 
>> Why doesn't __del__ run here?
> 
> Short anser: because nothing has removed the reference to the instance.

Hmmm. You're probably right, but not for the reason you think :-)


>> class Eggs(object):
>>     def __new__(cls):
>>         instance = object.__new__(cls)
>>         print("instance created successfully")
>>         return instance
>>     def __init__(self):
>>         print("self definitely exists:", self)
>>         raise Exception
>>     def __del__(self):
>>         print("deleting", repr(self))
> 
> This is a good example of why it helps to *never* call ‘__init__’ the
> “constructor”. It isn't, because ‘__init__’ acts on an
> already-constructed instance.

I don't believe I did call it the constructor :-)


> Hance, an exception raised from ‘__init__’ is not going to affect
> whether the instance exists.
> 
>> And an example:
>>
>>
>> py> e = Eggs()
>> instance created successfully
>> self definitely exists: <__main__.Eggs object at 0xb7bf21ec>
>> Traceback (most recent call last):
>>   File "<stdin>", line 1, in <module>
>>   File "<stdin>", line 8, in __init__
>> Exception
>> py>
> 
> 
> Right. The instance is constructed successfully (by ‘__new__’, the
> constructor). It will be bound to ‘s’, creating a reference to the
> instance.

Bound to `s`? Did you mean `e`?

It's certainly not bound to `e`, since that is unbound. I went back to my
interpreter session and tried to view the value of `e`, and as I expected,
it was unbound:


py> e
deleting <__main__.Eggs object at 0xb7bf21ec>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'e' is not defined


So it looks like what may have been holding onto the reference to the Eggs
instance was the exception or traceback, and once that got
garbage-collected (by me causing a new exception and traceback) the
finaliser ran.

Which both Oscar and Chris suggested, thanks guys!

So this isn't definite proof, but it is very suggestive that the thing
holding onto the reference to the half-built Eggs instance is something to
do with the exception.


> The exception raised from the initialiser does not stop you from binding
> the instance to a name. That binding succeeds; the reference remains.
> Nothing has caused that reference to go away.

That bit is actually wrong. Given:

name = expression

an exception in `expression` prevents the binding to `name` from occurring
at all. If `name` already existed, then it remains untouched, and if it
didn't it remains unbound.



-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list