[Python-Dev] Design question: call __del__ only after successful __init__?

Tim Peters tim_one@email.msn.com
Fri, 3 Mar 2000 11:49:52 -0500


[Tim]
>> Note that Java is a bit subtle:  a finalizer is only called
>> once by magic; explicit calls "don't count".

[Guido]
> Of course.  Same in my proposal.

OK -- that wasn't clear.

> But I wouldn't call it "by magic" -- just "on behalf of the garbage
> collector".

Yup, magically called <wink>.

>> The Java rules add up to quite a confusing mish-mash.  Python's
>> rules are *currently* clearer.

> I don't find the Java rules confusing.

"add up" == "taken as a whole"; include the Java spec's complex state
machine for cleanup semantics, and the later complications added by three
(four?) distinct flavors of weak reference, and I doubt 1 Java programmer in
1,000 actually understands the rules.  This is why I'm wary of moving in the
Java *direction* here.  Note that Java programmers in past c.l.py threads
have generally claimed Java's finalizers are so confusing & unpredictable
they don't use them at all!  Which, in the end, is probably a good idea in
Python too <0.5 wink>.

> It seems quite useful that the GC promises to call the finalizer at
> most once -- this can simplify the finalizer logic.

Granting that explicit calls are "use at your own risk", the only
user-visible effect of "called only once" is in the presence of
resurrection.  Now in my Python experience, on the few occasions I've
resurrected an object in __del__, *of course* I expected __del__ to get
called again if the object is about to die again!  Typical:

    def __del__(self):
        if oops_i_still_need_to_stay_alive:
            resurrect(self)
        else:
            # really going away
            release(self.critical_resource)

Call __del__ only once, and code like this is busted bigtime.

OTOH, had I written __del__ logic that relied on being called only once,
switching the implementation to call it more than once would break *that*
bigtime.  Neither behavior is an obvious all-cases win to me, or even a
plausibly most-cases win.  But Python already took a stand on this & so I
think you need a *good* reason to change semantics now.

> ...
> Sure, but the rule "if __init__ fails, __del__ won't be called" means
> that we don't have to program our __init__ or __del__ quite so
> defensively.  Most people who design a __del__ probably assume that
> __init__ has run to completion. ...

This is (or can easily be made) a separate issue, & I agreed the first time
this seems worth fixing (although if nobody has griped about it in a decade
of use, it's hard to call it a major bug <wink>).

> ...
> Sure -- but I would argue that when __del__ returns,
>__instance_construction_completed should be reset to false, because
> the destruction (conceptually, at least) cancels out the construction!

In the __del__ above (which is typical of the cases of resurrection I've
seen), there is no such implication.  Perhaps this is philosophical abuse of
Python's intent, but if so it relied only on trusting its advertised
semantics.

> I think that the proposed change probably *fixes* much morecode that
> is subtly wrong than it breaks code that is relying on __del__ being
> called after a partial __init__.

Yes, again, I have no argument against refusing to call __del__ unless
__init__ succeeded.  Going beyond that to a new "called at most once" rule
is indeed going beyond that, *will* break reasonable old code, and holds no
particular attraction that I can see (it trades making one kind of
resurrection scenario easier at the cost of making other kinds harder).

If there needs to be incompatible change here, curiously enough I'd be more
in favor of making resurrection illegal period (which could *really*
simplify gc's headaches).

> All the rules relating to __del__ are confusing (e.g. what __del__ can
> expect to survive in its globals).

Problems unique to final shutdown don't seem relevant here.

> Also note Ping's observation: ...

I can't agree with that yet another time without being quadruply redundant
<wink>.