Copy constructors

Alex Martelli aleax at aleax.it
Tue Aug 14 04:15:10 EDT 2001


"Guido van Rossum" <guido at python.org> wrote in message
news:cp3d6wf70d.fsf at cj20424-a.reston1.va.home.com...
    ...
> No, in the new system, __getattr__ is called for all attributes.  See

Oh!  OK, that *does* change everything (and I *DO* mean
everything).  If most of what I wrote in the post you
were answering didn't make sense to you, it may be simply
because it was based on how __getattr__ works today, as
I was considering the case of a "normal", "classic" class.
http://www.python.org/2.2/descrintro.html says the new
__getattr__ behavior only works for subtypes of built-in
types, not for normal classes...

For example:

> > 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?
>
> Sorry, I'm not following you at all here.  I suggest that you try
> again.

Re-read this under the hypothesis that __getattr__ is only called
for attributes not found by other means (like today), and it
should be clearer: under this hypothesis, to ensure __getattr__
can trap every access, there can be no other attributes in the
class (if there were, __getattr__ wouldn't be called when those
attributes are accessed).


>     value = _EmptyClass()
>     value.__class__ = klass # The class it should be
>
> and then it initializes the instance variables.
>
> I claim that it's more elegant to write
>
>     value = klass.__new__()

Sure, or value = new.instance(klass) today -- one line
is "more elegant" than two.  (Avoiding module new was
in my case primarily motivated by the stern warnings in
the doc "care must be exercised when using this module"
etc -- not sure what klass.__new__() is supposed to
provide better than new.instance(klass), although it
does save a whopping 4 characters:-).


> Zope's persistency support manages this without assignment to
> __class__.  A persistent object has three states: ghost, valid, and

Yes, and AMK has, it seems to me, posted eloquently as to
how and why this is a kludge (or at least that's how I
read AMK's posts on this thread), made necessary by the
extension-class disabling (effectively) __class__ changes.


> I really doubt that Python 1.4 was a much worse language than 1.5, and

I wouldn't know first-hand, not having used Python 1.4 myself.
But if 1.5 was so feeble an improvement, why did you bother
creating it at all...?-)

> I doubt that Python's popularity has much to do with the ability to
> assign to __class__.

It's one of, say, 100 things that are different in Python compared
to less-dynamic languages, so it may account for (null hypothesis)
1% of somebody choosing Python rather than something less dynamic.
(Similarly, since, e.g., Ruby does have the equivalent feature,
then, if it's taken away from Python, it may make Ruby preferable
where now Python would be chosen -- here the differences are just,
say, 10, so the null hypothesis here would be to have the lack
account for 10% of the migration).

> Besides, I'm not so much forbidding it as resisting the work it takes
> to implement safely.  You can write a metaclass that allows __class__
> assignment in C, and then its safety is your own responsibility.

I understand I can program everything and a half via metaclasses,
but that's nowhere near having a feature as a built-in part of
the language.  I made just the same argument about the get/set
property-idioms you've now added: they could easily be implemented
in the past (with __getattr__ or metaclasses), but having them
readily available in the language itself *IS* still important --
I'm not going to repeat that part, but it applies just as well
to other features such as change-of-__class__.

> > 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 like to see which is more common -- the need to change __class__
> or the need to inherit from a built-in type.  I'm betting the latter,
> by a large amount.

Most likely, yes.  People with significant Ruby or Smalltalk experience,
having both features available, could perhaps offer opinions with
a more solid, first-hand basis.


> > 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?)
>
> The architectural restrictions that make it hard to change __class__
> in some situations have been part of Python since it was first
> released.  Unless I misunderstand what you're asking for, 2.2a1 should
> exhibit this just fine.

But 2.2a1 lets me change __class__ "in some situations" -- just
not in all of them.  What you're now proposing (or so it appears
to me, and, it seems, to other discussants) is to take those "some
situations" away -- forbidding __class__ change except for the
last-ditch escape route of having a very special metaclass.  And
I still don't see why this should be inevitable from the 2.2a1
sources.  Even the restrictions on __class__ change you proposed
on another message would be better from my POV than a prohibition --
forcing the new class to be a subclass of the old one that adds
no slots (not so hard to check, not so hard to explain either).



> > 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.
>
> This is the most bizarre argument for __class__ asignment that I have
> seen so far.

It's called an "analogy", and it's not so much an argument for
allowing class change as a demolition of your "argument" that
class change can be removed with no worries because "there are
other ways of accomplishing" similar goals.

Having shown that "there being other ways" is not a good reason
for taking the direct-way away from the language (even if the
direct-way may have performance impact for naive users that
don't understand things too well -- just as ** typically tricks
naive users away from the more-efficient Horner polynomial:-) --
I haven't made an argument FOR retaining the existing feature,
but I _have_ removed an argument for _removing_ it.


> > make users request virtuality explicitly.  Like anybody who's ever
> > taught C++ or helped beginners in that language fix mistakes, I've
    ...
> Since you're invoking beginners here, let me bounce that argument
> right back.  Do you seriously believe that assignment to __class__ is
> something you would want beginners to know about?

Programs who are experienced in certain other languages but beginners
to Python -- sure!  In particular, I've been evangelizing Python to
reasonably-experienced C++ programmers, and the ability to do away
with letter/envelope idioms has invariably drawn a LOT of interest.


> > > 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.
>
> Your mind works so different from mine...  It's frustrating for me to
> try and understand what you mean, and no doubt it's the same for you.

Yep -- when I look at the language you've designed and the code
you've written, I feel I understand you well and admire you, but
when I read the English discourse with which you explain things,
I often find it infuriating -- not so much ununderstandable, but
apparently irrelevant, or restating the obvious, or ignoring what
you're seemingly answering to, for example.  [Good thing there's
Tim to channel you for the rest of us, sometimes!-)]


> I strongly believe that most Python users have no need for assignment
> to class attributes most of the time.  Class attributes are mainly
> used for two purposes: (1) methods and (2) default initializations of
> instance variables.  Neither use requires changing the class
> attribute.  Overrides are done using subclassing or assignment to
> instance variables.

But subclassing gives me new and different objects -- it does not
change the behavior of all *existing* instances of the class.  To
change the behavior of every instance by assignment to instance
variables, besides the circular-reference issue that Moshe Zadka
mentioned, I have to locate every instance of the class, which
requires substantial "plumbing" (a list of weak references, say),
needing design-foresight (and thus front-loading the design effort,
a very unpleasant need in methodologies such as XP) as well as
expending substantial amounts of memory (for the plumbing, AND
for the duplicated __dict__ entries in every instance).

I agree -- there's no need to assign to class attributes *most*
of the time.  But knowing whether this is one such time requires
lots of foresight, a front-loaded design effort, designing-in
infrastructure in my application for potential future needs, and
other such costly/anti-XP practices.  A smoothly-dynamic language
such as Python used to be is a great facilitator for lightweight
just-in-time-design methodologies such as XP -- I'd refactor
when needed but meanwhile I could try out new ideas *RIGHT NOW*...
because dynamic-configurability is the DEFAULT.

Another analogy: most system administrators don't need to take
network interfaces up and down dynamically, or reconfigure their
parameters, most of the time.  But when the need arises, it's
*WONDERFUL* to be able to do so without having to reboot each
and every time.  Having ifconfig and route and iptables &c thus
makes operating systems such as Linux vastly superior to ones
not _allowing_ for such dynamic reconfiguration -- because you
know you'll have the tools at hand on those rare but possible
situations where they ARE needed.  Sure, a typical administrator
may need to use those tools dynamically and interactively maybe
once in a few months, just for emergencies, once system
configuration is well-tuned -- but they also make it much
easier and faster to REACH a well-tuned configuration at the
start, as well as providing a safety net just by existing.

Most pilots of personal airplanes have no need for parachutes
most of the time.  It's still a good idea to have them around.

> I know some people like to use class variables
> instead of module globals, mimicking Java/C++ static class variables,
> but I believe that's mistaken -- those languages don't have a module
> namespace like Python, so the convention of putting globals in a class
> makes sense from a naming perspective there.

I'm not entering this specific debate -- I do agree with the
general sense that classes are overrated/overused as the basic
unit of organization, where modules might make more sense.  Do
note, however, that C++ *DOES* have an excellent namespace
system -- Java, like Eiffel, may be totally class-centered,
but that's one criticism [or praise:-)] that you cannot fairly
level to C++ as a language.


> Every time someone uses the dynamism to patch a standard library
> class, I cringe -- it's asking for trouble.

I think I understand this specific concern -- in classic Python
terms, the class might become a type (for speed reasons) in some
new release tomorrow, and then the patching would stop working
(if what was an attribute held in the class's __dict__ becomes
a hard-wired 'slot' of the type).

But you don't need to have __dynamic__ *DEFAULT* to 0 to ward
off THIS concern, Guido: since YOU control the standard library
classes, you may choose to put __dynamic__=0 directly or
indirectly into each and every one of them if you wish.  This
would be a close parallel to what the designers of Java did,
by tagging as "final" the standard library classes where they
did not want users to be able to extend them -- they didn't
need to have each and every class *DEFAULT* to 'final'!-)

You might also get as many grumblings for this as the Java
guys do from people who'd really love to extend, say, String,
but that's another issue -- they just forgot to add for each
'final' class an immediate ancestor, e.g. ExtendableString,
*without* the 'final'... similarly I'd hope Python would keep
allowing dynamic-versions of standard classes which have a
__dynamic__ of 0, although, if I understand you correctly,
this would need no special effort in the library -- users
can still subclass standard classes and redefine __dynamic__
in their own subclasses thereof.


Alex






More information about the Python-list mailing list