Copy constructors

Alex Martelli aleaxit at yahoo.com
Sun Aug 12 05:43:00 EDT 2001


"Guido van Rossum" <guido at python.org> wrote in message
news:cpzo969i4k.fsf at cj20424-a.reston1.va.home.com...
    ...
> > Are there any features that we can use any longer that are not 'at our
own
> > risk'?  Division will be changed, __class__ assignment is going away,
> > type() means something different, even the rules of scope...
>
> But you're getting so much in return!  Subclassing built-in types,
> get/set methods, class and static methods, uniform introspection...

Absolutely!!!  The prospects are WONDERFUL.  We're just quibbling
about the details: my deepest apologies if that makes us seem
ungrateful for the huge bounties you keep delivering.


> > Also, as far as I know, better alternatives do not exist; for example,
> > "promise" objects from a database which are latently initialized when
they
> > are accessed.
>
> If you know the type it's going to be eventually, you can use
> C.__new__() to create an uninitialized C instance.

I don't see how that would help -- maybe I'm being thick.  How would
the uninitialized C instance automatically initialize itself at need?

Let me give a real-life example (it was not done in Python, but it would
have been so much simpler if it had).  We have a "UI-engine" component
that our applications use for user-interfaces.  A typical application we
write has a few hundreds GUI-dialogs, each described in a file (or more
often in a DB entry).  One of the tasks of the UI engine is making the
dialogs transparent to the application-engine (AE).  The AE holds
references to dialog-objects and calls methods on them when it needs
to, either to make a dialog actually appear and let the user tweak
things (non-modally -- it's typical that the user wants to leave the
dialog half-finished, go back to checking other stuff with the app,
then come back and finish the dialog), or to change some details of
a dialog dynamically.

Originally we read all of the dialog-descriptions at start-up, but that
meant the start-up was far too slow.  Then we added an 'initialized'
status bit to the dialog -- but EVERY method of the dialog had to call
an 'initialize myself in case I'm not yet initialized' method at the
start... well not every method, we soon found out, because methods
that 'change details' (but don't show the dialog) should rather queue
up the change requests if the dialog is not yet initialized, and the
'actually inizialize' method must apply the pending changes -- but
if a 'change details' method is called when the dialog is already
initialized, it must immediately apply the change rather than queue
the change-request.  And there are methods which access some
dialog property (without showing the dialog) -- those need not
trigger initialization either, IF the property is set in a pending
(queued) change-request for the not-yet-initialized dialog...

The dialog class becomes far too large and unwieldy with this
darned status-bit.  It doesn't take much to realize that this is
badly factored; the way this WANTS to be is:
    -- Dialog is an abstract class (pure interface and a few
            template-methods and utility-methods),
    -- UninitializedDialog is a concrete subclass of Dialog whose
            methods do certain things and which holds a certain
            kind of state (queue of change-requests, filename or
            other info needed to find initialization information),
    -- TrueDialog is another concrete subclass of Dialog whose
            methods do very different things and hold a totally
            different kind of state
The only hitch is -- the SAME object needs to be born as
an UninitializedDialog and then BECOME a TrueDialog when
it's about to be shown (or when the transition can't be
avoided for other reasons).  The AE may be holding a lot
of references to 'yonder dialog', and is entitled to consider
a dialog as a unitary object -- it's the UI server's business
to handle these optimizations transparently.

The obviously right approach would be to have the Dialog
object *change class*, of course.  But we were working in
C++.  So, we ended up with a typical letter/envelope
idiom, LOTS of boilerplate so that the envelope can delegate
a zillion methods to the letter, and so on.

THIS is what I think Glyph has in mind when he talks of
Promise objects.  Now -- this falls in the case you describe:
I *do* know the object is eventually going to be a
TrueDialog (if the promise needs to be kept -- out of
many hundreds dialogs, a typical run of the application
may well only need to actually-show a dozen or two).
But how would TrueDialog.__new__() help me obviate
the lack of a way to change someDialog.__class__?  It
would give me an empty TrueDialog, but how would I
use it to solve the above-sketched problem...?


> I'd rather not because it's a complicated check to write, and it may
> be difficult to explain the restrictions.  Here's an example of the
> kind of restriction that is unavoidable.

Doesn't this fall into an "identity of slots" case?  If the __class__
can be changed only when the slots of the old and new class
are identical, isn't this decently easy to check and explain?


> > (If you implement "reference.become(other)", for example, I won't
> > mind nearly so much <0.5 wink>)
>
> I'm not sure what that means, but if you could live with weak
> references, we could easily add a way to change the referent of a weak
> reference object.

Now THAT might help in my case -- the UI server would hand out
to the AE *weak* references to the Dialogs, and change the
referent when needed.  The only issue would seem to be the
very fact that the reference is weak -- what is going to hold the
dialog object[s] alive just as long as the AE has some reference[s]
to that object and then garbage-collect them?  Maybe, as well as
changing the weak reference's referent, we need a way to ask
for a weak reference that isn't really weak -- one that DOES keep
the referent alive -- but IS 'reseatable'.  If such not-really-weak
references (plus maybe some indeed-weak ones) were all the
extant references to the object, then 'becomes' could be
implemented for that case, and I believe this might be OK
for the cases I have in mind.


> For example, we're looking into optimizing access to builtins.  For
> this, we need to assume that the __builtin__ module is immutable; in
> addition, if a module doesn't have a global 'len', for example, we
> have to assume that such a global won't be inserted into the module
> dynamically.  I'm only aware of a very small number of applications
> that violate this constraint; I'd rather provide a separate explicit
> mechanism to override built-in functions so that the optimizer can be
> aware of a potential change and avoid it.

Absolutely no objection to THIS: if a mechanism is provided, then
having it be more explicit to afford greater performance in the
normal case seems quite OK to me.  What I'm afraid of (regarding
the change of __class__) is the risk of being left without ANY
simple and workable mechanism for tasks that, while perhaps
rare, are very important .  (Regarding the __dynamic__=1, as
discussed on another post, I have a different issue, namely, what
should the default be; it seems to me that builtins being immutable
as a default, and only changed via explicit mechanisms, is a fine
match for the patterns of normal code, but *classes* being
immutable by default and needing explicit turning off of this
optimization isn't quite as smooth).


Alex






More information about the Python-list mailing list