Tim Peters tim_one at
Thu Jun 17 07:58:19 CEST 1999

[Tim, rambling]
> So my plan for the hour <wink> is:  if M&S finds a cycle, it
> will clean it all up iff no involved object has a __del__.  If
> an object in a trash cycle does have a __del__, tough, the cycle
> won't be reclaimed.  Instead we'll supply a function you can call
> if you care, that will return those objects (one at a time?  in
> a list?  whatever), and you do with them what you will; if you don't
> clean them up, you'll keep getting them back every time you call that
> function until you resurrect them or break the cycle by hand in
> the way that makes most sense to your algorithm.

[Evan Simpson, scrambling <wink>.]
> Presuming the hour isn't up yet, I've a handful of scattered comments:
> . How about a callback instead of/in addition to a probe function?

Dunno.  Not keen on burdening it with features before I think I know whether
it makes sense at *all* <wink>.

> That way you could flip a switch and get called even on cycles which
> are "del-clean", if you wanted to check for unintentional cycle creation.

A worthy goal indeed.

> . "del-cleanness" only requires that actual cycle participants lack
> __del__, not dangly bits, right?

"refcount rules" seem to work fine in the absence of cycles (at least nobody
has complained about them yet!), and once a cycle is purged the dangly bits
can follow those rules without further intervention.  So, yes, your reading
seems to make good sense.  For this hour, anyway <wink>.

> Or does this re-introduce analysis that you intended to throw out?

I'm trying to build on what Guido has in mind -- tracking only the dicts in
cycles is very attractive for time and space reasons, and there's sure no
analysis I could even dream of throwing out from *that* bare bones approach
<wink>.  But Guido is resorting to "well, let's ignore __del__ entirely
then" because it doesn't collect enough info to do anything more ambitious
than that.

What it leaves behind is a list of dicts definitely alive, and another list
of dicts definitely unreachable *and* definitely involved in a cycle.  What
I want to explore is whether we can build on that, by now making a deeper
analysis of *just* the stuff in the latter "doomed" list.  By running a more
expensive M&S with the dead dicts as "the roots", the structure of the trash
can be deduced and the objects containing those dicts identified (if an
object contains a dead dict, the object must itself be trash -- else the
dict could not be dead).

That would take additional time roughly proportional to the amount of cyclic
trash, and influenced by the complexity of its structure.  Which should be
"not much" and "not very" most often.  Then I wave my hands a lot and
everyone is happy <snort>.

> . (somewhat rhetorical) How the heck do you recognize what you've got when
> the machinery hands you an object which isn't otherwise reachable?

If you intend to use __del__ in objects involved in cycles, the obvious
answer is that it's your problem to make each object store enough info about
itself in itself so that your code knows what to do with it when it gets it
back; i.e., it's akin to asking what the heck you're supposed to do with a
floating-point number <wink>.

> I realize that there's no general answer to this, and that in some
> specific cases it would be enough to check __bases__, but the only answer
> which occurs to me off the bat is to uniquely mark objects which you
> expect to have to handle, or provide them with special __cleanup__
> or the like.

Whatever works for you is fine by me.  I personally expect I would use
__del__ in a cycle only to torture newbies on with the obscure
possibilities <wink>.

> Now we have wide-open policy, which I recall you decrying <wink>.

When a language *can* do everything "the right way" on my behalf & without
bothering me, I appreciate it when it does.  refcounting is like that.

But when it *can't* do "the right thing" by magic, it's obligated first to
do no harm, and second to give me a way to do it myself.  That's where the
wide-open policy comes in; e.g., if objects X and Y are in a dead cycle, and
X's finalizer happens to need the services of an active Y in order to shut
itself down cleanly, the language does harm if it runs Y's finalizer
first -- and may also do harm if it refuses to finalize either.  But the
language simply can't guess the intent of your code, so it should refuse to
act in ignorance, instead letting you do it explicitly however you require.
I don't think Python should fill in your "def" bodies with code it makes up
either <wink>.

> I guess that no matter what mechanism is implemented, certain
> cases will appear and need to be handled with "well, don't do that!"

No, no, that's never ever happened in *Python* <wink>.

> finalization-is-like-life-you-can't-win-and-you-can't-break-even-ly y'rs
> Evan Simpson

luckily-though-you-do-get-to-die-in-the-end-ly y'rs  - tim

More information about the Python-list mailing list