[Python-Dev] How to suppress instance __dict__?

Guido van Rossum guido@python.org
Sun, 23 Mar 2003 17:33:59 -0500


[David]
> >> It's almost too bad that the distinction between __new__ and
> >> __init__ is there -- as we find we need to legitimize the use of
> >> __new__ with things like __getnewargs__ it be comes a little less
> >> clear which one should be used, and when.  TIMTOWDI and all that.

[Guido]
> > __new__ creates a new, initialized object.  __init__ sets some values
> > in an exsting object.  __init__ is a regular method and can be called
> > to reinitialize an existing object (not that I recommend this, but the
> > mechanism doesn't forbid it).  It follows that immutable objects must
> > be initialized using __new__, since by the time __init__ is called the
> > object already exists and is immutable.

[David again]
> Shouldn't most objects be initialized by __new__, really?  IME it's
> dangerous to have uninitialized objects floating about, especially in
> the presence of exceptions.

Normally, there are no external references to an object until after
__init__ returns, so you should be safe unless __init__ saves a
reference to self somewhere.  It does mean that __del__ can be
surprised by an uninitialized object, and that's a known pitfall.
And an exception in the middle of __new__ has the same problem.

So I don't think __new__ is preferred over __init__, unless you need a
feature that only __new__ offers (like initializing an immutable base
class or returning an existing object or an object of a different
class).

> >> In the absence of clear guidelines I'm tempted to suggest that C++ got
> >> this part right.
> >
> > Of course you would.
> 
> Oh, c'mon.  C++ is ugly, both brittle *and* inflexible, expensive,
> painful, etc.  There must be at least _one_ well-designed thing about
> it.  Maybe this is it!

You said it. :-)

> > I tend to think that Python's analogon to C++ constructors is
> > __new__,
> 
> Yup.
> 
> > and that __init__ is a different mechanism (although it can often be
> > used where you would use a constructor in C++).
> >
> >> Occasionally we get people who think they want to call overridden
> >> virtual functions from constructors (I presume the analogous thing
> >> could be done safely from __init__ but not from __new__)
> >
> > Whether or not that can be done safely from __init__ depends on the
> > subclass __init__; it's easy enough to construct examples that don't
> > work.  But yes, for __new__ the situation is more analogous to C++,
> > except that AFAIK in C++ when you try that you get the base class
> > virtual function, while in Python you get the overridden method --
> > which finds an instance that is incompletely initialized.
> 
> Either one seems equally likely to be what you don't want.

Yeah, this is something where you can't seem to win. :-(

> >> but that's pretty rare.  I'm interested in gaining insight into the
> >> Pythonic thinking behind __new__/__init__; I'm sure I don't have the
> >> complete picture.
> >
> > __new__ was introduced to allow initializing immutable objects. It
> > really applies more to types implemented in C than types implemented
> > in Python.  But it is needed so that a Python subclass of an immutable
> > C base classs can pass arguments of its choice to the C base class's
> > constructor.
> >
> >> Nice. Too bad about 2.2.
> >
> > Maybe the new pickling could be backported, but I fear that it depends
> > on some other 2.3 feature that's harder to backport, so I haven't
> > looked into this.
> 
> Are people who don't want to upgrade really that much more willing if
> it doesn't involve a minor revision number?  I figure that if I
> supported 2.2 once, I'd have to be very circumspect about doing
> something which required an upgrade to 2.2.x.

The idea is that an upgrade from 2.2.x to 2.2.(x+1) won't break any
code, it will only fix bugs.  For example, Zope requires Python 2.2.1
because of a particular bug in Python 2.2[.0] that caused Zope core
dumps.  Of course, the "breaks no code" promise can't be true 100%
(because some code could depend on a bug), but we try a lot harder not
to break stuff than with a 2.x to 2.(x+1).  Even though there we also
try not to break stuff, we're less anal about it (otherwise the
language would just get uglier and uglier by maintaining strict
backwards compatibility with all past mistakes).

--Guido van Rossum (home page: http://www.python.org/~guido/)