[Edu-sig] Pass by Reference

David MacQuigg macquigg at ece.arizona.edu
Mon May 19 19:41:30 CEST 2008


Summarizing our earlier discussion, we now have some simple terminology and a model for what happens when a parameter is passed to a function in Python.  A variable in the calling program has a name and a pointer to an object, which has a type, value, and address in memory.

   variable:(name, pointer) --> object:(type, value, address)

When this variable is passed to a function, the pointer is copied to a new variable in the function's namespace.  The original object stays exactly where it was, and no copy is made.

Now we need to decide whether this process is a "call-by-reference", a "call-by-value", or something else.  There seem to be two schools of thought.  If we look just at the machinery above, we could say "clearly, it's a call-by-reference."  Some Python experts say "Not so fast!  It's the result that matters to users, not the underlying machinery." {see section 6.5.2 of John Zelle's Python Programming for a good presentation of the "call-by-value" point-of-view, and section 10.3.1 of Michael Goldwasser's Object-Oriented Programming in Python for a good presentation of the "call-by-reference" point-of-view.}

In the traditional definitions {Kernighan & Ritchie, 2nd ed. p.27} "call-by-value" means copying the original value from the caller to the function.  "Call-by-reference" means copying only a reference (pointer) to the original value.  The results, which students will remember, are that call-by-value is safe, and call-by-reference is efficient.  In a call-by-value, the original object is safe from any modification by the function.  In a call-by-reference, passing a pointer takes minimum time and memory, no matter how big the object is.

The problem is that Python doesn't exactly fit our expectations in using either of the traditional definitions.  If we look just at the machinery, its a call-by-reference.  Then we run into problems if we expect to modify the original object.  Unlike C, there is no way to simply stuff a value into a location specified by a pointer.  The result is much like call-by-value, at least for immutable objects.

If look just at this result, and say "call-by-value", we run into problems with mutable objects, and have to resort to explanations that students find vague and counter-intuitive, like - "the object itself is passed" or, "the value IS the pointer".

My preference at this point (perhaps my engineer's bias) is to say Python is "like call-by-reference".  That keeps the explanation a lot closer to the underlying machinery (which engineering students will remember).  It is safer to err on the side of call-by-reference (no nasty surprises with mutable objects).  The apparent deviations from the simple model can be explained with concepts that students need to learn anyway (mutability and re-binding).

I said earlier that we shouldn't raise this topic in a class for beginners, but now I think it is quite appropriate for college freshmen.  I'm an electrical engineer, not a computer scientist, but I do remember learning about call-by-value/reference decades ago.  I think there is value in keeping the definitions simple, even if modern languages don't conform.  Let's not muddy the waters with "call by object" or other ill-defined concepts.  There really are only two ways to pass an argument to a function - copy the value or copy a reference.

-- Dave


At 01:48 PM 5/16/2008 -0700, David MacQuigg wrote:

>If there are no objections to my models and terminology, the next step is to describe exactly what happens when we pass an argument in C and in Python.
>
>C is very helpful, since there is nothing hidden in this primitive language, and we can see exactly what is happening.  Also, there is no confusion about the definitions of "call-by-value" and "call-by-reference".  See Kernighan & Ritchie, 2nd ed. p.27.  Let's call these the "traditional" definitions.  In C there are only two ways to pass an argument - copy its value or copy a reference (pointer) to that value.
>
>Again, the model and terminology for C in this discussion is:
>
>   variable:(name, type, pointer) --> object:(value, address)
>
>The variable name, type, and pointer are kept by the compiler.  The compiled module has just the actual data bits representing the value stored at a particular address.
>
>Here is a simple program that calls a function with two parameters, one called by value, the other by reference.  
>f(int a, int *b) { 
>    a += 1;   // modifies a local variable 
>   *b += 10;  // modifies the caller's variable 
>}
>main() { 
>  int x = 0; 
>  f(x, &x);   // pass the value and the pointer 
>  printf("x = %d\n", x); 
>}
>// output is x = 10 
>In the call to function f, the value of the caller's variable x is copied to the function's parameter a.  The function then operates on its own copy of this value, and the original data value is safe from any modification by the function.  This is "call-by-value" in C.
>
>In that same call, the address of the caller's variable x is copied to the function's parameter b.  Operations on b then change the value of x, but not its type or address.  This is "call-by-reference" in C.
>
>In Python, there is only one way to pass an argument.  There may be pointers flying around, but we never see them.  We will have to infer what is going on by doing some tests. Again, the model and terminology for our discussion is:
>
>   variable:(name, pointer) --> object:(type, value, address)
>
>Here is a simple program that calls a function with one parameter.
>
>   >>> def f(b): b[:] = range(9)
>
>   >>> x = ['abc', 'xyz']
>   >>> f(x)
>   >>> x
>   [0, 1, 2, 3, 4, 5, 6, 7, 8]
>
>In the call to function f, we must infer that a reference to the caller's variable x is copied to the function's variable b.  Operations on b then change the value of x, but not its type or address.
>
>I know there are later complications ( What if x is immutable?  What if b gets re-assigned within the function? ), but right now I would like to focus on exactly what happens in the call to function f.
>
>I'll pause once more to see if I've made any mistakes, before the third and final step - deciding what to call this thing that Python does in passing an argument.
>
>-- Dave
>
>
>At 12:41 PM 5/15/2008 -0700, David MacQuigg wrote:
>>At 04:30 PM 5/14/2008 -0500, John Zelle wrote:
>>
>>>At some point, I have to just let this go, as I think we all on this
>>>list have a pretty good understanding of the differences between C and
>>>Python in terms of assignment and parameter passing. But let's _not_ use
>>>the term "pass by reference" when talking about Python. You CANNOT
>>>CHANGE THE CONTENTS OF THE VARIABLE THAT IS SUPPLIED AS AN ACTUAL
>>>PARAMETER. It will still refer to the same object (contain the same
>>>reference) regardless of what is done to the formal parameter. Hence,
>>>the variable IS NOT passed by reference. The value of the variable
>>>(which happens to be a reference) is copied. Pass by value is the
>>>accepted label for this mechanism. Pass by reference means something
>>>else (as I've pointed out in previous posts).
>>>
>>>Christopher's point on teaching the differences directly is, I think, a
>>>good one. But we don't need to worry about that for beginners.
>>
>>I agree, the topic should not come up in a class for beginners, but the question I got was from a student in a class on C.  In that situation, it does help to explain Python's calling mechanism in terms that these students understand.  I've posted my answer at http://ece.arizona.edu/~edatools/ece175/Lecture/QnA.txt
>>
>>I used the phrase "like pass-by-reference" to avoid the controversy as much as I can.  As you can see from the references cited in my answer, the terms call-by-value and call-by-reference do not have universally accepted definitions, even among experts.
>>
>>The differences seem to come down to what we mean by the words "value" and "reference".  Everyone seems to have unstated assumptions about their meaning, probably depending on what language they are thinking about.  Perhaps it will help if we can agree on models for variables in C and Python.  Here are my models:
>>
>>In C, the type of an "object" (char, int, float, etc.) is associated with
>>the variable name, not the object.
>>
>>   variable:(name, type, pointer) --> object:(value, address)
>>
>>In Python, type is associated with the object, not the variable name.
>>
>>   variable:(name, pointer) --> object:(type, value, address)
>>
>>As you can see, I've used the word "value" to mean the actual data associated with the object, and the word "pointer" to mean whatever it is that references the object (a real pointer in C, I assume a C-pointer in Python).
>>
>>I realize my diagrams may bias the discussion, so perhaps I should stop at this point and ask for alternative models or terminology.
>>
>>-- Dave
>
>
>_______________________________________________
>Edu-sig mailing list
>Edu-sig at python.org
>http://mail.python.org/mailman/listinfo/edu-sig




More information about the Edu-sig mailing list