[Python-Dev] Garbage collecting closures
Paul Prescod
paul@prescod.net
Mon, 14 Apr 2003 12:32:06 -0700
Jeremy Hylton wrote:
> The details of when finalizers are called is an implementation detail
> rather than a language property.
and
Guido van Rossum wrote:
> ... Since then,
> the rule has always been "some arbitrary time after nothing refers to
> them." And the corollary is "always explicitly close your external
> resources."
I knew I'd hear that. ;) Overall, I agree. Anyhow, I'll give you some
background so you can understand my use case. Then you can decide for
yourself whether it is worth supporting.
When you're dealing with COM objects, you do stuff like:
foo = a.b.c.d
b, c and d are all temporary, reference counted objects: reference
counted on both the COM and Python sides. It is quite inconvenient to
treat them as "resources" like database handles or something.
a = Dispatch("xxx.yyy")
b = a.b
c = b.c
d = c.d
a.release()
b.release()
c.release()
80% of the variables in my code are COM objects!
I'm not a big win32com programmer, but it is my impression that this is
NOT the typical programming style.
COM is specifically designed to use reference counting so that
programmers (even C++ programmers!) don't have to do explicit
deallocation. COM and CPython have roughly the same garbage collection
model (reference counted) so there is no need to treat them as special
external resources. (nowadays, Python cleans up circular references and
COM doesn't, so there is a minor divergence there)
The truth is that even after having been bitten, I'd rather deal with
the 3 or 4 exceptional garbage collection cases (circular references
with finalizers, closures, etc.) than uglify and complicate my Python
code! I'll explicitly run GC in a shutdown() method.
Even though it is easy to work around, this particular special case
really feels pathological to me. Simple transformations set it off, and
they can be quite non-local. From:
Safe:
def a():
if something:
a()
def b():
a()
... # ten thousand lines of code
x = com_object
to
Buggy:
def b():
def a():
if something:
a()
a()
... # ten thousand lines of code
x = com_object
OR
Safe:
def b():
def a():
if something:
a()
a()
... # ten thousand lines of code
com_object.do_something()
to
Buggy:
def b():
def a():
if something:
a()
a()
# ten thousand lines of code
junk = com_object.do_something()
If I'm the first and last person to have this problem, then I guess it
won't be a big deal, but it sure was confusing for me to debug. The
containing app wouldn't shut down while Python owned a reference.
Paul Prescod