FW: Strange behaviour of __del__

David Bolen db3l at fitlinxx.com
Tue Dec 11 07:01:45 CET 2001


pzw1 at hotmail.com (Peter Wang) writes:

> the __del__ method is only called when the reference count of an
> object reaches 0.  in your example, "a" is actually still referenced
> when you reach the interpreter prompt after the exception is thrown,
> because the traceback object contains the stack frame, which in turn
> references "a".  (check out
> http://www.python.org/doc/current/ref/customization.html, read the
> bold "Programmer's Note")  so, when you manually execute "del a", that
> decreases the refcount to 0 and that in turn causes "a" to be deleted.
> 
> the NameError exception after that is confusing, i don't know why it's
> doing that.  probably the destructor itself throwing an exception.

I think you're mostly on the right track except for the part about del
actually being responsible for the decrement of the reference count.
The object itself is definitely being kept alive by a reference in the
traceback (via the 'self' local in one of the traceback frames), which
will keep the object alive while the "last" traceback is held by Python.

E.g.:

    >>> a = Script1.A(1)
    Traceback (innermost last):
      File "<stdin>", line 1, in ?
      File "Script1.py", line 5, in __init__
	raise RuntimeError
    RuntimeError
    >>> print sys.last_traceback.tb_next.tb_frame.f_locals
    {'self': <Script1.A instance at 7e2710>, 'a': 1}

But there is never a binding to 'a' in the interpreted namespace.  I
expect what's happening is that as the interpreter prepares to execute
the next statement (e.g., the "del a") it clears the stored traceback
information to prepare for the next command.  It is this operation
that invokes the __del__ method (and prints the output) indirectly,
and not the actual command entered.

Then, when the command is actually executed, the interpreter correctly
claims that there is no "a" via the NameError.  It's just the sequence
of output that makes it look like it's in response to the del.

I'm not sure of precisely when the traceback is cleared (and it seems
to be somewhat Python version specific), but for example, the same
thing can happen even if the next command isn't del:

    >>> a = Script1.A(1)
    Traceback (innermost last):
      File "<stdin>", line 1, in ?
      File "Script1.py", line 5, in __init__
	raise RuntimeError
    RuntimeError
    >>> print a
    In __del__()...
    Traceback (innermost last):
      File "<stdin>", line 1, in ?
    NameError: a

--
-- David
-- 
/-----------------------------------------------------------------------\
 \               David Bolen            \   E-mail: db3l at fitlinxx.com  /
  |             FitLinxx, Inc.            \  Phone: (203) 708-5192    |
 /  860 Canal Street, Stamford, CT  06902   \  Fax: (203) 316-5150     \
\-----------------------------------------------------------------------/



More information about the Python-list mailing list