passing by refference

Doug Quale quale1 at charter.net
Thu May 15 16:28:38 EDT 2003


Tim Peters <tim.one at comcast.net> writes:

> [Doug Quale]
> > Python is call-by-value.  The values are objects, but the argument
> > passing mechanism doesn't say anything about what values are in the
> > language.
> 
> Sorry, that didn't make sense.

You're right.  I meant the values are references to objects.  I
apologize, that was a bad slip.

> It's true that, in CPython, pointers to
> objects are passed under the covers, in C's call-by-value fashion, but
> that's an implementation detail, and has no visible effect on the Python
> language's semantics.  Call-by-object really is the best description of
> Python's semantics.  If you do a literature search, you'll also see that
> described as "call by object reference" and "call by sharing".
> Call-by-value it ain't -- unless you view the implementation reference as
> "the value", but that simply isn't helpful to anyone except Python's
> implementors (of which Jeremy and I are two, BTW).
> 
> >>> def f(x):
> ...     x[:] = [-1] * 3
> >>> y = [1, 2, 3]
> >>> f(y)
> >>> y
> [-1, -1, -1]
> >>>
> 
> There's no rational sense in which that can be called call-by-value.

No, that's exactly what call-by-value means when applied to Python values
(actually r-values).  What happens when you try this:

>>> y = [1, 2, 3]
>>> x = y
>>> x[:] = [-1]*3
>>> y
[-1, -1, -1]

Same behavior, no function calls.  This behavior occurs because of the
nature of Python values, not because of anything interesting or unusual
about the argument passing mechanism.

Call-by-value has nothing to do with the implementation and everything
to do with the observed behavior.  (I know you're a Python
implementor, but you're still wrong about this.)

"Call-by-value

This is, in a sense, the simplest possible method of passing
parameters.  The actual parameters are evaluated and their r-values
are passed to the called procedure." (Compilers: Principles, Techniques
and Tools by Aho, Sethi and Ullman).

In order to make sense of the argument passing mechanism you have to
know what a value is in the language.  Actually you have to know what
an r-value is in the language, since there are two types of values in
programming languages, l-values and r-values.  These terms just refer
to values used on the left and right sides of an assignment.  The
value of interest in argument passing is the r-value.  Your Python
code is a good example of both l-value and r-value.  It appears that
r-values in Python are always object references.  (If this is not the
case then I'm sure you can correct me.)  Python l-values are more
complex.  Often they are names that are bound or rebound to the
r-value, as in

y = [1, 2, 3]
foo.x = 42

Some Python l-values are more easily considered as representing
locations rather than names, such as your example x[:] in

x[:] = [1] * 3

(There might be ways that you could consider x[:] to be a name rather
than locations, but that seems much more complex.)  (In most
programming languages descended from the Algol family tree l-values
are most easily thought of as locations that are stored to.  In many
other languages like Scheme and the functional programming languages
l-values are better thought of as names that are bound.)

If the value passed to a function is the same value that would be used
on the right hand side of an assignment, the language is
call-by-value.  Now you see the rational way in which Python is called
cbv.  It really is that simple.

> A similar program using C structs shows what call-by-value does mean:
>
> #include <stdio.h>
> 
> struct s {
>     int x, y, z;
> };
> 
> void display(const char* tag, struct s s) {
>     printf("%s %d %d %d\n", tag, s.x, s.y, s.z);
> }
> 
> void f(struct s s) {
>     display("inside before", s);
>     s.x = s.y = s.z = -1;
>     display("inside after", s);
> }
> 
> void main() {
>     struct s t = {1, 2, 3};
>     display("before", t);
>     f(t);
>     display("after", t);
> }
> 
> Of course that displays:
> 
> before 1 2 3
> inside before 1 2 3
> inside after -1 -1 -1
> after 1 2 3

Yes, and yet here you injure your own argument again.  Naturally you
are correct: C is call-by-value.  If we used arrays instead of
structs, everyone knows what would happen:

#include <stdio.h>

void display(const char* tag, int s[]) {
    printf("%s %d %d %d\n", tag, s[0], s[1], s[2]);
}

void f(int s[]) {
    display("inside before", s);
    s[0] = s[1] = s[2] = -1;
    display("inside after", s);
}

void main() {
    int t[] = {1, 2, 3};
    display("before", t);
    f(t);
    display("after", t);
}

Of course that displays

before 1 2 3
inside before 1 2 3
inside after -1 -1 -1
after -1 -1 -1

Why are arrays different?  You and I both just said that C was call by
value.  Well, C is call by value.  The r-value of a C struct is the
struct, but the r-value of a C array is a reference to the first
element in the array.

Some people might explain it by saying that in C, scalars and
structures are passed by value and arrays are passed by reference.
Not only would that be needlessly complex, it would be wrong.  C array
r-values are references, and pass-by-reference would then require that
a reference to a reference be passed and that's not what happens.  C
arrays are passed in exactly the same way that they are assigned.  In
fact every C value is passed in exactly the same way that it is
assigned.  That's why C is call-by-value.  Python is the same.

Values in Pascal and Python are much simpler than in C because C
r-values are somewhat confusing.  (In particular, arrays and structs
are treated differently in C for no apparent reason.  Of course it was
a historical accident.)  Python r-values are references to objects.
Some Python objects are mutable and some are immutable.  Python
argument passing is call-by-value.

The odd thing is that I'm sure that you and Aahz both know all this.

Anyone who understands how assignment works in Python already
understands how argument passing works.  That's the true nature of
call-by-value.  Trying to say it's something else just means that you
have to explain the same things all over again when you describe how
assignment works.  In Python, if you say

y = [1, 2, 3]
x = y

is an assignment of the value of y to x (or better binds the name x to
the value of y), then

f(y)

passes the value of y to f().  This is call-by-value.




More information about the Python-list mailing list