assignment to __class__ (was Re: Copy constructors)

Guido van Rossum guido at python.org
Mon Aug 13 23:34:07 EDT 2001


"Alex Martelli" <aleaxit at yahoo.com> writes:

> "Guido van Rossum" <guido at python.org> wrote:
> >     http://www.python.org/2.2/descrintro.html
> 
> I thought I had understood it, but still don't see where it mentions
> "x.__class__ = whatever" now being forbidden or restricted?

That one's easy. :-)  Assignment to __class__ is not documented for
classic classes either, so I didn't think it was necessary to mention
it.

> > It would be relatively easy to allow __class__ assignment only if (a)
> > the new class is a subclass of the old class, and (b) the size of the
> > new instance is the same as the old instance.  Would this be sufficient?
> 
> Does 'size' in this context mean 'number of slots'?

Close enough.  In combination with the subclass requirement, the
"equal size" requirement means that the subclass doesn't add any
slots, and that guarantees the safety requirement.

But without the subclass requirement, in order for the __class__
assignment to be safe, things are more difficult.  E.g. a list
instance has two "slots" beyond the basic object header: a size and a
pointer.  Now this class:

  class C(object): __slots__ = ['a', 'b']

also has two slots following the basic object header.  But switching a
C instance to a list or back would be a disaster, because the
interpretation of the slots is different.

> In this case, albeit
> with somewhat peculiar contortions (requiring new.classobj or the
> equivalent), I think it would be sufficient for all cases that come to
> my mind -- I'd just have to put any extra attributes in the __dict__
> (which I do today for *every* attribute anyway:-).

Sorry, I think you're off on the wrong foot there.

> The "generate empty object through __class__ assignment" trick
> would also become sort-of-possible again (although of no practical
> interest whatsoever:-) -- to wit:
> 
> def make_empty_copy(any_object):
>     klass = any_object.__class__
>     class Empty(klass):
>         def __init__(self, *args, **kwds): pass
>     newcopy = Empty()
>     class Full(Empty):
>         __init__ = klass.__init__
>         __name__ = klass.__name__
>     newcopy.__class__ = Full
>     return newcopy
> 
> the Full class is not really the same as any_object.__class__, but
> nobody's gonna find out (presumably) since it's undistinguishable
> under normal use of isinstance or any behavior-test whatsoever
> (or have I forgotten to copy some needed attribute for that?).

I don't think this is a useful kludge.  And how is newcopy going to
acquire all the othe rattributes of any_object.__class__?

> These classes Empty and Full are examples of what I mean above
> as roughly 'equivalent' to new.classobj calls:-).  I think I could
> handle 'real' cases of class-change (worst case) through similar
> means, i.e. generating on the fly a class that's basically what I
> need but formally inherits from the object's original class so it
> can be assigned.  I realize this will not support _deleting_ any
> method wrt the original class, but that's not a need in any case
> that easily comes to mind.

It would be easier to bite the bullet and write the metaclass (in C)
that does the proper safety check.

> Yes, but, when servers DO have to stay up, fixing them on the fly,
> albeit indeed fragile, is a specified constraint.  I guess in some cases
> one could devise alternate strategies: putting up a new fixed server
> process on a different machine, port-redirecting all new requests to
> the new machine, and finally pulling down the old buggy server when
> the conversations for the requests it was serving at fix-time are gone.

I'm assuming you have to plan for this anyway, since you'll need a way
to doctor the server in the first place.  So why not make your
planning easy by having __dynamic__ = 1 somewhere?

> *blink* I had never thought Python's philosophy was protecting
> "typical users" from themselves -- I thought that was the idea
> of Pascal, Modula-2, &c, to Eiffel, the languages that know what's
> good for you better than you know yourself, so they'll force you
> to program the way Wirth (or Meyer) KNOWS is the one right
> way to program.  As I previously read your recent posts, I thought
> that the __dynamic__ thing was about performance instead...?

Here we go again.  I mention one reason and it is assumed that this is
the only reason.  As I've said before, my mind works a lot faster than
my fingers can type (it seems it's the opposite for you :-), and
sometimes the rationalization for an idea comes only gradually.

If you think about it, Python does a lot to protect typical users from
themselves (why otherwise do you think it's gaining success as an
educational language)?  For example, an input line of a million bytes
won't cause a buffer overflow.  For example, arithmetic overflow is
not silently truncated.  For example, almost anything that could cause
a core dump is caught before it does (and we fix the remaining core
dumps in real time :-).  For example, mixing incompatible types in
expressions causes a TypeError rather than having a random undefined
side effect.  For example, using an undefined variable name raises a
NameError rather than silently being equal to zero.  For example, the
whole division thing.  And so on.

> Can we expect variable declarations a few minor releases from
> now, then?  That would presumably be consistent with the new
> focus on protecting typical users from themselves.

I'm presuming you're being sarcastic.  We may indeed see *optional*
variable declarations -- but not required ones.  But realistically,
even the optional declarations seem far away -- the types-sig is only
active for about 6 weeks per year, and most of that time is used
rediscovering where we were a year ago... :-(.

> "Of course" as long as the extra devices don't significantly
> interfere with the tools' previous strengths in terms of cost and
> power.  When the significant interference is there, there is no "of
> course" about it -- it becomes a highly problematical trade-off.

Exactly.  What's going on here is that I'm trying to tease out how far
I should go with the safety device without making the tool unusable.
The new classes in Python 2.2 (which are entirely optional -- by
default you get the same classic classes as in 2.1 and before) are one
step of a new design.  We'll see how it needs to be tweaked.

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



More information about the Python-list mailing list