[Python-Dev] How to suppress instance __dict__?

David Abrahams dave at boost-consulting.com
Tue Mar 25 16:23:20 EST 2003


====== DISCLAIMER ========

I'm not claiming Python is broken.  I just like to think about
language design issues.

====== DISCLAIMER ========

Alex Martelli <aleax at aleax.it> writes:

> <posted & mailed>
>
> David Abrahams wrote:
>
>> "Delaney, Timothy C (Timothy)" <tdelaney at avaya.com> writes:
>> 
>>> Because most of the time we want to work with fully-constructed
>>> objects. You can't use overridden methods in a constructor - only
>>> from an initialiser.
>> 
>> Even if that were true, it's seldom needed (only comes up rarely in
>> C++ where you can't do that) and often incorrect in any case
>> because the derived class isn't constructed yet.
>
> I think you're dismissing the issue a tad too glibly.  In C++, I
> often found myself needing the "two-phase initialization" pattern:
> a costructor that did the VERY MINIMUM needed to ensure some very
> fundamental invariants on the object -- then, a framework would
> ensure that an ordinary virtual method "initialize" got called
> to give the object a chance to do substantial initialization work
> before the object got totally hooked up to the framework and
> started being used in earnest.  The key issue was often that, for
> a constructor, you could never be sure if the object WAS fully
> constructed (or if you were in a base class) -- no such problems
> with 'initialize', where you could rest serene that you WERE
> executing in the "leaf class", with a fully constructed object,
> period.

I'm not convinced of your need yet.

> For example say that the purpose of my class is modeling a
> window object in some underlying windowing system.  The actual
> window object must be costructed only once, and it must be
> constructed with detailed parameters as determined/overridden
> by the leaf class -- so no constructor of a class that might
> possibly be used as a BASE class dare invoke the underlying
> window system's "create window" operation, because the ctor
> of more-derived classes haven't been run yet so it's uncertain
> which parameters should in fact be used for that operation.
> Whence, the need for two-phase init -- the underlying window 
> is constructed in the 'initialize' (second phase) so the leaf
> class has full power to control window creation parameters.

The usual answer to that is: "don't rely on a virtual function call to
get those parameters; instead have the derived class pass them to the
base class constructor".  TOOWTDI.

> Classes modeling connections to external entities of many
> kinds often have similar issues -- i.e. from this POV there
> is no necessary difference between "creating the underlying
> window object" and "opening the connection to the database",
> say -- in both cases there are creation/opening parameters
> and the potential need to override them, etc, etc.

Ditto.  Those are constructor parameters.

> I can find many more use cases of "two-phase init", but this by
> itself should already suffice to explain that it's anything but
> "seldom needed"... in C++.

I'm not convinced yet, and I've been around C++ programming long
enough to feel pretty confident about what's commonly needed... but
I'm still willing to be convinced ;-)

Furthermore, being disciplined about fully-initializing objects in the
constructor has positive effects on the complexity of invariants and
on exception-safety.  Anytime I find myself thinking about virtual
calls from constructors I return to that discipline and am reminded of
how well it works.

> ctor's just labor under too many constraints to make them fully
> satisfactory for all cases of initialization needs.

I didn't say "all"; just the great majority.

> How this translates to Python -- I'm not sure.  The __new__
> constructor, at first blush, would seem to be just as flexible
> as the __init__ initializer.  However, some issues are
> apparent.  It's easy for an __init__ to delegate to that
> of a superclass; it's NOT easy to see how similar delegation
> is supposed to take place for __new__ -- just as an example.

Oh.  Well that's a problem for my argument, then.  I have to admit to
not knowing much about the mechanics of __new__.  However, it might
also be an indicator that a simpler initialization scheme might have
worked well for Python if __new__ were different than it is.

> It's certainly possible to arrange for the latter, mind you:
> __new__ does have a cls parameter so it may costruct objects
> of different classes from the one I'm coding it in -- so if
> everything is coded with extreme care perhaps there are no
> real obstacle cases.  But consider MULTIPLE inheritance --
> how would you arrange for THAT...?  I.e.:
>
> class Ba1(object):
>     def __init__(self):
>         self.a1 = 23
>         super(Ba1, self).__init__()
>
> class Ba2(object):
>     def __init__(self):
>         self.a2 = 45
>         super(Ba2, self).__init__()
>
> class Der(Ba1, Ba2):
>     def __init__(self):
>         super(Der, self).__init__()
>         self.b = 67
>
> Using __new__ instead of __init__, how does class Der,
> derived from two independently coded bases, ensure all
> initialization done by the bases (here symbolically
> represented by mere attribute setting) is properly 
> performed on the new object of class Der being created,
> before finishing it up as it likes (here with just
> yet another attribute setting)?

Oh, right, because __new__ essentially does the allocation, yes?
Maybe two orthogonal ideas that should be separate are bound together
in __new__.  If we had one allocator and one initializer it would be a
different (better?) world.

-- 
Dave Abrahams
Boost Consulting
www.boost-consulting.com




More information about the Python-list mailing list