What's better about Ruby than Python?

Alexander Schmolck a.schmolck at gmx.net
Tue Aug 19 18:23:56 EDT 2003


Alex Martelli <aleaxit at yahoo.com> writes:

> Alexander Schmolck wrote:
>    ...
> > To recap: usually, if I change a class I'd like all pre-existing instances
> > to become updated (Let's say you develop your program with a running
> 
> If you CHANGE a class (mutate a class object), that does indeed happen.

Indeed. But see below.

> 
> You seem to have some deep level of confusion between "changing an
> object" and "rebinding a name (that happened to be previously bound
> to a certain object) so it's now bound to a different object".

I don't think I'm confused about this.

> 
> Rebinding a name *NEVER* has any effect whatsoever on the object that
> might have, by happenstance, previously bound to that name (except that,
> when _all_ references to an object disappear, Python is free to make
> that object disappear whenever that's convenient -- but that doesn't
> apply here, since if a class object has instances it also has references
> to it, one per instance).

Is your assumption of my ignorance regarding the difference between
mutation/rebinding maybe based on the implicit assumption that a class
statement by some sort of logical necessity *has* to create a new class object
and then bind a name to it? 

Is there a reason why a class X(...):... statement can't just mutate a
preexisting, former version of the class X (provided there is one?). Maybe
there is, in which case let me state that I'm not particularly hung up on
this, what I'm hung up about is the ability to easily update the behavior of
pre-existing instances.

How exactly this happens (by mutating the class in question (by whatever
means, a new class statement, some function calls, __dict__-assignment), by
rebinding the .__class__ for all instances etc.) is of less importance to me
(although I suspect the 2nd alternative would cause all sorts of problems,
because the old (viz non-'id'entical) class object could also hang around in a
couple of other places than .__class__, .__bases__ etc.).

> 
> This rule is universal in Python and makes it trivially simple to
> understand what will happen in any given occasion -- just as long
> as you take the minimal amount of trouble to understand the concepts
> of names, objects, binding (and rebinding) and mutation.  If a language
> has no such clear universal rule, you're likely to meet with either
> deep, complex trouble, or serious limitations with first-classness
> of several kinds of objects.  In Python everything is first-class,
> and yet there is no substantial confusion at all.
> 
> Repeat with me: rebinding a name has just about NOTHING to do with
> mutating an object.  These are VERY different concepts.  Until you
> grasp them, you won't be a very effective programmer.

Agreed, but I think that happened some time ago.

> 
> > python session; you notice a mistake in a method-definition but don't want
> > to start over from scratch; you just want to update the class definition
> > and have this update percolate to all the currently existing instances.
> 
> I assume that by "update the class definition" you mean that you want
> *necessarily* to use some:
> 
> class X: ...
> 
> statement that defines a NEW class object (which may happen to have
> the same name as an existing class object -- quite irrelevant, of
> course).

I simply want some effective mechanism to have changes in selected parts of my
sourcecode adequately reflected in a running interactive session, without
much fuzz. 

I'm happy to write some elisp and python code to do some extra work on top of
py-execute-region on a key-press. By "adequately reflected" I mean amongst
other things that I can easily change the behavior of existing instances of a
class after I made changes to the source code of that class.

> OK, then, so arrange (easily done with some function or
> editing macro, if you have a decent editor / IDE) to do the following:
> 
> oldX = X    # save the existing classobject under a new temporary name
> 
> class X: ...   # define a completely new and unrelated classobject
> 
> oldX.__dict__.clear()
> oldX.__bases__ = X,
> 
> # in case you also want to change the old class's name, you might:
> oldX.__name__ = X.__name__  # so you're not constrained in this sense
> 
> # optionally, you can now remove the temporary name
> del oldX
> 
> There. What's so hard about this? How does this fail to meet your
> desiderata?

I tried this some time ago and discovered it only worked for old style
classes; the dict-proxies of new-style classes are, I think, not directly
mutable, I'd guess for efficiency reasons.

Here is an example of the difference (update, clear, del won't work either,
BTW):

>>> class X:
...     def bar(): "bar"
... 
>>> X.__dict__['bar'] = lambda :"foo"
>>> class X(object):
...     def bar(): "bar"
... 
>>> X.__dict__['bar'] = lambda :"foo"
TypeError
TypeError: object does not support item assignment

> Wouldn't it have been easier to ask, in the first place, "I would like to
> obtain this effect, is there a Python way" (being a BIT more precise in
> describing "this effect", of course !-), rather than whining about "Python
> can't do this" and complaining of this as being "unreasonable"?

Agreed, this was an unproductive way to describe my problem. Mea culpa.

Although I have investigated the problem before (and for the reason stated
above, settled for the suboptimal reassigning to .__class__ when I
*desperately* wanted to change a important instances), on second thoughts not
only should I have phrased myself differently, I should also have spent more
time thinking about it. 

I *think* it ought to be possible to effect all the necessary changes by
directly manipulating attributes, rather than going via .__dict__, even for
class/staticmethods.

It's too late now, so I haven't fully thought this through yet but I'll give
it a try tomorrow.

Anyway, I still don't see a compelling reason why class statements
couldn't/shouldn't be mutating, rather than rebinding. Is there one?

> 
> 
> > AFAIK doing this in a general and painfree fashion is pretty much
> > impossible in python (you have to first track down all instances -- not an
> 
> I personally wouldn't dream of doing this kind of programming in production
> level code (and I'd have a serious talk with any colleague doing it -- or
> changing __class__ in most cases, etc).

I'd be interested to hear your reasons. (The only argument against this style
that I can think off from the top of my head is that it might encourage sloppy
and code development and unwitting reliance on irreproducible artifacts of the
session history. 

I don't think this problem occurs in practice, however. If I write
(non-throwaway) code, I always write plenty of testcode, too, (which, BTW I
also find *much* more pleasant to develop and debug interactively) and
frequently start new python processes to to run this testcode in fresh
sessions to satify myself that things just don't appear to work because of
accumulated garbage in my interactive session.

[snipped]
> I don't think the fact that nobody complains is strongly connected to the
> fact that most Python users grasp the possibility of updating a class by
> modyfying its __dict__ etc; indeed, one great thing about Python is that
> most users typically tend to SIMPLICITY rather than "cleverness" -- if they
> wanted to modify a class X's method Y they'd code it as a function F and
> then simply do X.Y=F -- extremely simple, rapid, effective, and no need
> to much with redoing the whole 'class' statement (a concept I personally
> find quite terrible).

I think we need to sharply distinguish between two things (and I should
presumably have stressed this more in my last post):

1. Redefining methods of class during execution, as part of the solution to
   the problem the program is supposed to adress.

2. Redefining classes as part of an incremental development process. In this
   case, you of course "redo" the whole class statement, because you edit the
   source code with the class statement and everything else in it and then the
   most convinient way to update your code is to send all or part of this
   newly edited file to your interactive session (plus potentially some
   tweaking code that mutates the pre-existing "live" version of X as you
   outlined above, or somehow else effects the changes you want to take place
   in existing instances).

I'm chiefly interested in 2. (to the extent that I consider it an essential
ability of a good programming language) and much less so in 1. (although
modifying instances and classes can also sometimes be useful).


> Rarely does one want to modify an existing class object any more 'deeply'
> than by just redoing a method or two, which this utterly simple assignment
> entirely solves -- therefore, no real problem.
> 
> If your style of interactive programming is so radically different from
> that of most other Pythonistas, though, no problem 

(I wonder whether it is (quite possibly) and if so why? Developing code
interactively just seems like the obvious and right thing to do to me.)

> put some effort in understanding how things work (particularly the
> difference between *MODIFYING AN OBJECT* and *REBINDING A NAME*), Python
> supports you with power and simplicity in MOST (*NOT* all) metaprogramming
> endeavours.
> 
> If you DO want total untrammeled interactive and dynamic metaprogramming
> power, though, *THEN* that's an area in which Ruby might support you
> even better

No it doesn't, for example Ruby doesn't have docstrings which is an
unnecessary waste of my time (apart from the fact that Ruby offends my
sense of esthetics).

> -- for example, this Python approach would *NOT* work if
> oldClass was for example str (the built-in string class -- you CANNOT
> modify ITS behavior).

> Personally, for application programming, I much prefer clear and
> well-defined boundaries about what can and what just CANNOT change. But if
> you want EVERYTHING to be subject to change, then perhaps Ruby is exactly
> what you want. 

Nope, in that case I'd almost certainly use smalltalk which I consider
infinitely superior to ruby (or possibly some Lisp).

> Good luck with keeping track of what names are just names (and can be freely
> re-bound to different objects) and which ones aren't (and are more solidly
> attacked to the underlying objects than one would expect...).

I don't think we really fundamentally disagree. I don't think one should screw
around with everything just because one can and I also think that languages
who set inappropriate incentives in that direction are less suitable for
production software development.

'as




More information about the Python-list mailing list