5

I noticed something about CPython's object deallocation which piqued my curiosity. Let's say I define a type that prints a message from its tp_dealloc function:

static void pyfoo_Bar_dealloc(pyfoo_Bar* self)
{    
    PySys_WriteStdout("Bar freed\n");   
    self->ob_type->tp_free((PyObject*)self);
}

I've also done the right thing with the allocator:

PyMODINIT_FUNC initpyfoo(void) 
{
    PyObject* m;

    pyfoo_BarType.tp_new = PyType_GenericNew;
    /* ... */
}

I compile it and run a Python 2.6 interpreter in the directory with pyfoo.so:

>>> import pyfoo
>>> a = pyfoo.Bar()
>>> a = None
Bar freed
>>> quit()

This is what I'd expect... the reference count drops to zero and the Bar object is collected. But now if I do:

>>> import pyfoo
>>> pyfoo.Bar()
<pyfoo.Bar object at 0x7f7c6d8f2080>
>>> quit()
Bar freed

... the Bar object is not collected until the interpreter exits. But surely the reference count on the Bar object created anonymously is zero, just like the one explicitly assigned. So why is it not deallocated immediately after creation?

(I'm doing this on Debian Squeeze with Python 2.6.6 compiled with GCC 4.4.5. I know it's not a "bug", I know Python-the-language doesn't place any particular constraints on Python interpreters this... I just want to know what is going on under the hood that makes it ignore anonymous objects like this.)

detly
  • 29,332
  • 18
  • 93
  • 152

2 Answers2

9

Because after you called pyfoo.Bar() the object is still accessible using the special object _

This works with pure Python, by the way:

class X:
    def __del__(self):
        print 'deleted'

And later:

 >>>a = X()
 >>>a = None
 deleted
 >>>X()
 <__main__.X instance at 0x7f391bb066c8> 
 >>> _
 <__main__.X instance at 0x7f391bb066c8>
 >>> 3 # causes _ to be reassigned
 deleted
 3

Notice how reassigning _ implicitly deleted the X object?

luispedro
  • 6,934
  • 4
  • 35
  • 45
  • 1
    Aha! Great answer, thanks. Now I see that if I just write a script that instantiates it anonymously and waits before exiting, it is indeed GCed immediately. – detly Nov 09 '10 at 02:45
  • Sorry to dredge this up again, but do you know if a similar thing happens with the most recently generated exception? – detly Dec 10 '10 at 07:22
  • It does; while any relevant exception handlers are running, the exception is available as one of the members of sys.exc_info(), and I vaguely recall it's stored somewhere else as well after the handler finishes running although I'm not 100% sure about that. – javawizard May 28 '13 at 22:53
3

Because you're using the REPL, which stores the last result in _. So it isn't really anonymous after all.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358