Inheriting the @ sign from Ruby

Alex Martelli aleaxit at yahoo.com
Tue Dec 12 12:26:38 EST 2000


"Roy Katz" <katz at Glue.umd.edu> wrote in message
news:Pine.GSO.4.21.0012121027001.14542-100000 at y.glue.umd.edu...
> On Tue, 12 Dec 2000, Alex Martelli wrote:
>
> > 'self' is not "pasted in front of a name" -- 'self' is an object,
> > and it is used with perfectly regular syntax for object access.
>
> Yes, we understand this.  Passing 'self' (or whatever people choose to
> call it) to an instance method is automatic upon invocation, and manual
> for anything else.  So we have:

I'm not sure what you mean in this context by 'invocation'.  Consider:

    foo = FooClass()

    f = foo.method
    g = FooClass.method

    f('pap')
    g(foo, 'pap')

The passing of the first-argument object is automatic "upon
invocation" of a BOUND-method-object; it's NON-automatic
("manual", if you wish) "upon invocation" of an UNBOUND-
method-object.  (Where in each case I am taking 'invocation'
as a synonym of 'call').

So, I'm not clear what exact distinction you're trying to
draw here by saying 'upon invocation' as against "anything
else".  Aren't the calls of f and g 'invocations' both?
Or do you distinguish as 'invocations' some of the calls
to a callable object, depending on said object's nature?
If the latter, then I've never heard of such a distinction.


> To me, grammatically and practically, self *is* pasted in as the first
> argument.  I'm bringing up a grammatical question.  My question was
> whether or not the shorthand was practical.

When you call a bound-method, the first argument can be,
I guess, said to be 'pasted' (strange terminology!).  But
that has nothing to do with the USE of the first (or
other) arguments in the body of functions that receive
them.  Such arguments are used with uniform syntax, no
matter whether they're the first, or not -- just the
same, identical syntax as that available for ANY use
of ANY other object.

Your 'shorthand' singles out ONE subcase (attribute
access via dot-notation) out of all, for ONE object
(the one received by a method as its first argument)
out of all, introducing an alternative way of
expressing that single, specific combination out of
all the huge variety; in other words, it introduces
a special-case, ad-hoc irregularity in a perfectly
simple and regular scheme.  This is one of the reasons
I find it a terrible idea (and from other responses
I've read on this thread, it seems to be a widespread
opinion).


> > So, you would like '@identifier' to be a special-case
> > syntax meaning exactly the same as 'self.identifier' (if
> > the first argument, as per normal convention, is named
> > 'self').
>
> I would like the @identifier to denote <self-object>.identifier, where
> <self-object> is the first parameter to a method. Regardless of whether it
> is 'self' or 'myself';  the 'def bind(myself, x)' was just an example.

If the singled-out argument is not named 'self', as it normally
is, readability will suffer anyway, I keep asserting.  The
'shorthand' will do nothing substantial to help, and if it
encourages such breaches of convention it will positively
hinder, the current uniformity of usage.


> > such a violation of
> > normal expectations will make your code harder to read
> > and understand by any maintainers.
>
> Why should there be any violation if all instance identifiers are labelled
> with a @? To be honest, I believe that the @identifier convention would
> decrease the number of variant programming styles.  We all want that

How can it possibly DECREASE them?  It would introduce ONE MORE way
to express the SAME, IDENTICAL thing, and thus it can only INCREASE
variability of expression, as some code says '@goo' while other
code keeps saying 'self.goo' instead.

> 'self' be used (instead of any other name).  Fine! the @identifier
> scheme would at least reduce this, then. You wouldn't see 'myself' (or
> some other deviant) pasted in front of every instance variable; you'd
> see a common @ sign. So for reading someone else's code, that's a good
> thing.

Absolutely NOT.  If lulled by the @-signs in most places, I'm LESS
likely to notice that the code's author has wantonly called the
self-object 'myself', while 'self', if it exists at all, may refer
to some other object (say a global).  So when the time comes that
the self-object is used, or should be used, e.g. in calling a base
class method, or getattr, etc, etc, it becomes MORE likely, not
less, than the confusion will prove damaging.

*EXPLICIT IS BETTER THAN IMPLICIT* -- a very important guiding
principle of Python programming.
    localvar=self.goo
is more explicit than
    localvar=@goo
in that, if, for example, I need to change this for maintenance
to
    localvar=getattr(self,'goo',None)
(a reasonably frequent occurrence), I don't have to unravel the
implicitness -- I *KNOW*, because I read it *right there*, the
name of the object that I need to pass to getattr (etc etc).


> > > What faults or strengths do you see with this?
> >
> > I see no strength in this supplementary syntax form
> > offering a second, different way to express what can
> > be perfectly well expressed explicitly today.
>
> shorthand??

Is saving 4 characters, '@' vs 'self.', so important as
to warrant the 'fork' in coding styles, the redundancy
itself, etc?  Not to me.  Most Pythonistas seem to be
even more minimalist, begrudging such new constructs as
list-comprehensions (which save a lot more than 4 chars,
and even have some 'conceptual' advantages).


> > I see it as a fault that all this does is offer a
> > second, different way to express what can be perfectly
> > well expressed explicitly today.
>
> Save for being able to call the first parameter of a method whatever you
> like, wouldn't this reduce the number of programming conventions?

Maybe there's an implication I find it hard to swallow: would
you want to make 'self.foo' ILLEGAL, breaking _all_ existing
Python code?  Because, if not, it appears to be obvious that
some code will keep saying 'self.foo' while other says '@foo',
and I really don't understand how you can possibly claim the
obviously-false 'reduction' in 'number of conventions'...

> > It would also be a (small) step along the road of
>
> I disagree; Like I mentioned above, this would enforce
> standardization.

...or the 'enforcement of standardization', either.


> > turning the currently crystal-clear syntax of Python
> > into that morass of special-purpose, ad-hoc, 'convenient'
> > kludges that make certain other languages' typical
> > programs look so peculiarly similar to line noise.
>
> Python's language is not crystal-clear, imho.  If we're so tough on

I was (and am) claiming crystal-clear *SYNTAX*.

> perl for having a kludgy interface, why do we have {} for
> defining dictionaries instead of [ key:value ]?

Because the Python object denoted by the syntax:
    [ whatever_you_want ]
is *ALWAYS* a list, and a dictionary is not a list.  Breaking
this 'always' would hardly enhance language-clarity!


> Why do we have to
> explain to new Python programmers how the label-value system Python uses
> is different than traditional call-by-reference and
> call-by-value?

Sez who?  People I've seen with Python as their first
language, and people I've seen coming from Java (which
has exactly the same argument-passing mechanism), seem
to have no trouble (there are many other languages which
work this way, but I have not observed Python users who
started out in any of those other languages).  I'm not
sure what you mean by 'label-value', but the semantics
of argument-passing, in Python, is exactly that of the
'assignment' operator, which simplifies the exposition
of both (the great uniformity of the Python treatment of
references is quite beneficial).

When passing any argument, the receiving function always
gets a reference to that argument, usable exactly as any
other reference to the same object would be (e.g., any
variable bound to that object, any list-item bound to
that object, any dictionary-entry bound to that object),
and so on.  If the object thus 'passed' is immutable, it
cannot be changed through the reference the function has;
if it offers methods by which it can be changed, those
methods may be called -- exactly as would be the case if
the reference was a list-item, dictionary-entry, whatever.

Re-binding the reference only affects the reference, not
the previously-bound object, nor any other reference, for
any of these kinds of references (arguments just as well
as any other).  You can't get any simpler, more uniform,
or clearer, than this.  (Calling re-binding 'assignment'
*MAY* be slightly-confusing terminology to some people).

People whose only language exposure, previous to Python
(or Java, etc) was to languages whose argument-passing
mechanisms differed greatly may be confused, I guess. But
the fact that Python (or Java) differ on this from other
languages hardly touches on their _clarity_.  Actually,
the issue is more likely to be with '=', SO-CALLED
'assignment' in Java or Python, ACTUAL assignment in
Fortran, C, C++.  The '=', in Fortran, C, or C++, will
normally "copy bits from one place to another", i.e.,
'assign' stuff.  In Java or Python, '=' _rebinds the
reference on its left-hand side so it now refers to
the result of the expression on its right-hand side_
(if any bits are copied to perform this, it will be the
few bits the language internally uses to keep track
for each reference of the issue 'to what object does
this reference refer right now' -- language-internal
issues of small import to its conceptual-model).


> (I've been programming in Python for three years now
> and I understand only that passing an int or float is call-by-value,
> otherwise it is call-by-reference.

Wrong, in terms of those languages where call-by-value
and call-by-reference have meaning (and, not coincidentally,
'=' or equivalent similar syntax DOES perform assignment).

Take C++, for example:

Foo foo(1);
Foo bar(2);

void afun(Foo& a, Foo& b)
{
    Foo temp(0);
    temp = a;
    a = b;
    b = temp;
}

afun(foo, bar);


Now THIS is call-by-reference: the assignment operators
which afun calls on its formal arguments a and b affect
the actual arguments passed, foo and bar.  Unless class
Foo redefines the assignment-operator operator= to
something VERY peculiar, this sequence will leave the
global variables 'foo' and 'bar' ``swapped'' in terms
of contents after the call to afun.

In Python, on the other hand:

foo = Foo(1)
bar = Foo(2)

def afun(a, b):
    temp = a
    a = b
    b = temp

afun(foo, bar)

all the *re-bindings* (SO-CALLED 'assignments', but
not really assignments!) that THIS afun performs on
its LOCAL VARIABLES temp, a, and b, have ABSOLUTELY
NO EFFECT WHATSOEVER on the actual-arguments foo
and bar: they are absolutely unchanged and pristine
after the call to afun, referring to exactly the
same objects to which they referred before it.

So, modeling Python's argument-passing as "by reference"
in this case (although it's an "otherwise") is simply
wrong, in terms of (for example) C++'s references (but
Visual Basic, Fortran, and other languages which DO
have pass-by-reference and actual assignment work much
like C++ does here).


> At the risk of naivete, I would
> like a simple & referencing operator so that I can explicitly,
> finally, have control over this.)

You have full control, and clarity, NOW -- if you take
the trouble to understand a little bit better how Python
really works, rather than proposing changes to it.

> When would I ever use print>> ?

In my humble opinion, never; I consider it a hateful
kludge, and its insertion in the Python language the
worst single thing that ever happened to Python in
its (almost) 10-years history.

OK, so, I guess even I (and I know few people prouder
than me) can allow the BDFL *one* terrible mistake
per 10 years or so...:-).

> Isn't there a less-ugly way of generating a raw string, instead of placing
> an 'r' in front of it?

Not that I know, though other languages have tried all
sorts of slight syntax-sugar variations (using " to
delimit non-raw, and ' to delimit raw-strings, for
example).  The r-decoration seems just fine to me.


> Good that you mentioned that the language is deteriorating. IMHO, from

I have said no such thing.  I said Python WOULD deteriorate
IF the @ special-case syntax was added to it (the only
deterioration I ever saw happen to Python was the recent
addition of the >> special-case syntax for print).


> the above, it's seemed like this from the start. (It's still my favorite
> language).

I'm really surprised that you have not taken the trouble
to grasp the references/objects semantics of Python, and
how it works uniformly and smoothly (in argument-passing
just as everywhere else), in 3 years, for what you say
it's your favourite language.

It's really a very, VERY simple idea -- though I hardly
ever see it explained as explicitly as it deserves to be,
and in fact the common idea of calling '=' "assignment"
(used in the Python docs, too) does its own little part
to obscure it.


Alex






More information about the Python-list mailing list