The script you wrote is creating a reference cycle in a less than obvious fashion. The non-obvious cycle is a result of all class
declarations being inherently cyclic, so the simple existence of a class
declaration in W
means there will be some cyclic garbage. I'm not sure if this is a necessary condition of all Python interpreters, but it's definitely true of CPython's implementation (from at least 2.7 through 3.6, the interpreters I've checked).
The thing that loops in your Z
instance and triggers the behavior you observe is that you use v
(which is a reference to a Z
instance) with closure scope when you declare Form.x
as part of the class
declaration. The closure scope means that as long as the class Form
defined by the call to W
exists, the closed upon variable, v
(ultimately an instance of Z
) will remain alive.
When you run a module with IDLE, it runs the module and dumps you to an interactive prompt after the code from the module has been executed, but Python is still running, so it doesn't perform any cleanup of the globals or run the cyclic GC immediately. The instance of Z
will eventually be cleaned (at least on CPython 3.4+), but cyclic GC is normally run only after quite a number of allocations without matching deallocations (700 by default on my interpreters, though this is an implementation detail). But that collection may take an arbitrarily long time (there is a final cycle cleanup performed before the interpreter exits, but beyond that, there are no guarantees).
By commenting out the line referencing v
, you're no longer closing on v
, so the cyclic class
is no longer keeping v
alive, and v
is cleaned up promptly (on CPython's reference counted interpreter anyway; no guarantees on Jython, PyPy, IronPython, etc.) when the last reference disappears.
If you want to force the cleanup, after running the module, you can run the following in the resulting interactive shell to force a generation 0 cleanup:
>>> import gc
>>> gc.collect(0) # Or just gc.collect() for a full cycle collection of all generations
Or just add the same lines to the end of the script to trigger it automatically.