Copy constructors

Alex Martelli aleaxit at yahoo.com
Sun Aug 12 16:03:22 EDT 2001


"Guido van Rossum" <guido at python.org> wrote in message
news:cpelqhf62b.fsf at cj20424-a.reston1.va.home.com...
> "Alex Martelli" <aleaxit at yahoo.com> writes:
>
> > I don't see how that would help -- maybe I'm being thick.  How would
> > the uninitialized C instance automatically initialize itself at need?
>
> For example, you could have a __getattr__ that initialized the thing
> as soon as it is touched.

But then *none* of the methods could belong to the class?!  Each and
every one to be inserted afresh in every instance's __dict__?!  But then
what's the use of "making the object the right class from the start",
as that class C HAS to be empty to let __getattr__ do its job?!

Of course, if the initialization is done by __getattr__ anyway, it's
quite futile to use __new__ to "return an empty instance of the
class" -- the necessarily-empty __init__ does just as well, no?  So,
again, I ask -- how is knowing from the start the class C to which
the instance will belong, when its Promise is fulfilled, and exploiting
the __new__ feature to make an empty C.__new__ -- how does
this *HELP AT ALL*?  Your alleged "example" is NOT an example of
all of this glittering technology helping at all with this problem.

Which is not a problem in Python as it stands -- becomes a problem
if and when you implement the "enhancement" (?) of forbidding the
change-class-of-object idiom that Python now supports.


> > Let me give a real-life example (it was not done in Python, but it would
> > have been so much simpler if it had).
>
> I see your reputation for being verbose is not a fable. ;-)
>
> This pattern can be programmed in lots of ways -- changing class is
> just one way, and not necessarily the best.

It's patently obvious that the 'becomes'/class-change way is not the
only way to implement this, considering I described two others.  It's
also pretty obvious to me that class-change is by far the most natural
implementation -- except when targeting a crippled language that
doesn't support class-change.

We need polymorphic behavior along a given interface (mostly
abstract, though it can supply some Template methods [in the
Gof4 terminology] and utilities).  There are several implementations
of this interface, each requiring distinct code and state.  A very
obvious case for placing each implementation in its own class.

Except we need this dual behavior, and switch from one to the
other, to be present in "the same object" from the point of view
of client-code.  So, if a language forbids an object's class to
change, it can't "really" be the same object -- we're forced to
add a layer of indirectness, one way or another.  I still think
letter-envelope is the best idiom for this in C++.  Automatic
delegation does ease the pain where present, as you suggest:

> Note that Python's dynamicism also allows other solutions to be coded
> more efficiently than in C++ -- e.g. coding a proxy in Python is a
> breeze using dynamic method lookup.

Again a class with just a __getattr__ -- an "indirector".  Pretty
powerful idiom, of course, as per the recipe I posted to:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52295.
But it's ironic -- there, I used an indirector to work around the
inability of inheriting from a built-in object.  Now, I look forward
to pensioning off that idiom... and I may have to resurrect it to work
around the inability of changing an object's __class__...!-)


> > > 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?
>
> That's the check I meant.  The problem is that the info about which
> slots are defined is spread all over the place, and some of it
> (e.g. slots accessed only in C) is not available for introspection.

I guess I'll have to look at the source to understand why that's
so inevitable -- right now it escapes me.  (Is it going to be part
of 2.2alpha2, or do I have to get a CVS tree?)


> > > 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

Thinking about it, it help's because it's an indirector -- a built-in
one.  It would similarly help in other cases where indirectors help,
even when changing-class is anyway inapplicable.

> > 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.
>
> I imagine you could probably do this the way persistency is typically
> done, but I haven't thought about it that much.

I think it would suffice to add an option to the existing weak
references to let them incref/decref their referent (in addition
to letting them be reseated, which you say will be easy).

A kludge that's come to mind since, usable if weak refs were
reseatable but still weak, is to give the client a *pair* of
references -- one normal, one weak, initially both to the
fake-object.  The client's tasked to only use the second item
of the pair to address the object, but warned that it is letting
go of the FIRST item that may invalidate the object.  When
the fake object needs to mutate to a true one, it reseats the
weak-reference and sets in itself a normal-reference to the
true object it's generating.  Peculiar indeed, but...

> > 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.
>
> It was introduced in Python 1.5, and I never really missed it before
> -- I just added it because I noticed it was easy to add.  I think it's
> not even documented, or if it is (I couldn't find it) it's marked
> experimental.  I still think there are other ways of accomplishing the
> same effect, maybe using a proxy pattern.

Of course, just as there are other ways of accomplishing raising-to-
power, besides having it as a built-in -- it IS just repeated
multiplication, after all.  That's no argument for removing **
from the language, it seems to me.  It IS no doubt rarely used
AND easily mis-used [code such as x**3+2.2*x**2+1.4*x+3.6
rather than 3.6+x*(1.4+x*(2.2+x)) is most likely slower] -- but
that's no real argument for taking ** away, as it may well be the
most natural/clear/readable way to express some problems, and
also (if and when used appropriately) faster than alternatives.


> A dynamic class will be slower than a static class, so I prefer having
> to request this explicitly.  But we can certainly quibble about that!

As I said, exactly Stroustrup's reasoning in C++'s design -- a non
overridable method is faster than a virtual one, so he preferred to
make users request virtuality explicitly.  Like anybody who's ever
taught C++ or helped beginners in that language fix mistakes, I've
had occasion to bemoan that design choice repeatedly -- even though,
with performance such an important priority for C++, and historical
compatibility reasons, it's quite understandable.  It appears to me
making static classes the default is a similar choice, requiring lots of
up-front design (dynamic classes are used to change behavior of
a running program, for example -- to refactor, unless dynamic
classes have been specified beforehand for all important places,
the program needs to be stopped), facilitating premature optimization,
and without Stroustrup's particular excuses (surely performance is not
as key a criterion for Python as for C++, and in this case we'd be
going _against_ historical compatibility).

> I expect that the new facilities will be seriously user-tested only
> after 2.2 is released, and experience will learn how often people are
> changing class variables.

You think people will be changing class variables _less_ often than
now because of 2.2's new facilities?  I don't see the relevance, but
I guess I must have missed something.  The new facilities appear
to be very deep and important, but in areas quite different from
those in which class-object changes are useful, it seems to me.


Alex






More information about the Python-list mailing list