[Edu-sig] Pass by Reference

John Zelle john.zelle at wartburg.edu
Tue May 20 22:59:35 CEST 2008


Hi David,

It's enough for me at this point that we all agree on what we're talking
about. The rest is a matter of "taste," I think. The one real sticking
point I still have with your approach is simply that what you "expect"
pass-by-value to do in Python (namely make a copy of the object that a
variable refers to) is not to my knowledge implemented in any language.
Lots of languages have been built around "pass-by-value," and none is
doing what you consider your acid-test for pass-by value. On the flip
side, any language that claims pass-by-reference as part of the language
allows me to do what I consider the acid test for pass-by-reference
(namely I can change the referent of the calling argument), which I
cannot do in Python. So I prefer to use the terms the way they have been
used historically and remain consistent with language design
traditions. 

As I've tried to emphasize, this is not really an issue of how Python
passes parameters, but rather how it treats variables. I base my
terminology on the traditional view of what a variable is, and that
still works quite well as a consistent model for Python. Doing so allows
me to use traditional terminology for parameter passing without any
imprecision or change of meaning. Your model of a variable is not the
traditional model (as you point out). That doesn't make your model
better or worse, just different. It does mean that your model will not
work for describing languages like Pascal, Fortran, C++, or Java.  But
your model is exactly the way I encourage my students to think about
Python. I guess you are arguing for a clean break with old traditions
and I am suggesting that they can co-exist (perhaps not to everyone's
satisfaction). I think both of those positions have merit. 

--John


 
On Tue, 2008-05-20 at 10:29 -0700, David MacQuigg wrote:
> Hi John,
> 
> I did some more searching on this question, and I see it has been discussed many times before.  I read a few of the threads, and they seem to go on forever with no resolution, just a lack of communication.  It comes down to the definition of "value".  One camp says a value is only the actual data in an object, the other says the value can be the reference.  I've been using it to mean only the data.
> 
>    variable:(name, pointer) --> object:(type, value, address)
> 
> Looks like this controversy is not just in the Python community.  I found a good discussion in Bruce Eckel's Thinking in Java, 2nd ed, Appendix A: Passing and Returning Objects.  On p. 1018, he has a discussion of pass-by-value with a nice summary of the views of "two distinct camps".  He concludes with "I will attempt to sidestep the issue.  In the end, it isn't that important.  What is important is that you understand that passing a reference allows the caller's object to be changed unexpectedly."
> 
> I think that is what we need to focus on.  What will students remember in later years?  Which explanation will be most beneficial in helping them avoid problems?
> 
> I hope that you will recognize the validity of other views, and not just say they are wrong.  I think that is the only way this discussion can move forward.
> 
> I wrote a detailed response to your last post, but decided to delete most of it.  I'll just leave a few parts where I need to correct a serious misunderstanding of what I am saying.
> 
> At 06:36 PM 5/19/2008 -0500, John Zelle wrote:
> 
> >You insist on writing/thinking/reasoning as if Python variables
> >"contain" the objects they refer to, but that simply is not the case for
> >the traditional use of the term "variable."
> 
> That is not at all what I meant.  In my model the variable has only a name and a pointer.  The variable refers to an object, but it does not "contain" the object, in the sense that a variable in C is like a box containing a value.
> 
>    variable:(name, pointer) --> object:(type, value, address)
> 
> > Literally, your model,
> >with the traditional definition would require that every assignment
> >statement in Python create a new variable. Consider this sequence:
> >
> >a = 3 # creates the variable "a"
> >a = 4 # "a" is now bound to another location
> >a = 5 # "a" is now bound to yet another location
> >
> >Since the address of the value changes with each assignment, the pair
> >(name, location) is changing each time, and hence we have here three
> >different variables with the same name. That's certainly not the way I
> >think about what's happening in Python!
> 
> In my model, the pointer associated with the variable named "a" is moved from one object to the next, leaving the old object for the garbage collector.
> 
> >So, my question is why don't we just stick with the traditional notion
> >of a variable being a (name, location) pair.
> 
> I've been calling it a (name, pointer) meaning the same thing.
> 
> > Realize that Python
> >dereferences the values for us when we use variables in expressions and
> >stop trying to invent new names for a parameter passing mechanism that
> >is identical in function to traditional call by value.
> 
> We seem to be coming from different traditions.  In my tradition, call-by-value means copying the actual data to the function, and call-by-reference means copying a reference (pointer).  These definitions include all possibilities, and lead to simple inferences about safety and efficiency, which is the only benefit I can see for discussing the topic with students who are nowhere near being language theory specialists.  The message to my students will be simple - It's like call by reference in C - efficient, but watch out for functions that might modify the objects you send as arguments.
> 
> The alternative - like call by value but ... seems a lot more difficult to explain.
> ... but it is efficient, because the "values" being passed are really just pointers.
> ... but it is not safe, because the function can modify the caller's arguments.
> 
> When I first learned Python, four years ago, I found the discussion in Martelli's book on call-by-value to be confusing, so much so that it impeded my understanding of the simple mechanism that it really is.  This confusion was not resolved until last week when I started researching this question, in response to a student's question in class.  I believe that telling students of C that Python is "call by value" will generate the same confusion that I suffered.  No amount of explanation, or re-assurance "this is what some experts say" will help.
> 
> The examples and diagrams in your section 6.5.2 are excellent.  Just for fun, I re-wrote that section keeping everything the same, but changing only the few paragraphs where you talk about call-by-value.  In my humble opinion, that section is shorter and much more clear from a "call-by-reference" point of view.  It avoids having to say that the "values" of variables are actually the pointers, not the data.  It avoids introducing more complex definitions (e.g. "call-by-reference" means that assigning a value to a formal parameter changes the value in the calling program.  That's a result, not a definition, and the result depends on unrelated details of the language, like re-binding semantics.)  It avoids the awkward "Isn't that interesting?" paragraph where you have to explain that in some cases you really can change the caller's object after it has been passed by value.
> 
> I'm not saying your definitions are wrong, I just don't see the benefits of adopting these more difficult and more specialized definitions.  Being consistent with some language theorists is not a benefit if it confuses students.
> 
> -- Dave
> 
> 
> >On Mon, 2008-05-19 at 10:41 -0700, David MacQuigg wrote:
> >> 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
> >> 
> >> 
> >> _______________________________________________
> >> Edu-sig mailing list
> >> Edu-sig at python.org
> >> http://mail.python.org/mailman/listinfo/edu-sig
> >> 
> >-- 
> >John M. Zelle, Ph.D.             Wartburg College
> >Professor of Computer Science    Waverly, IA     
> >john.zelle at wartburg.edu          (319) 352-8360  
> 
> 
> _______________________________________________
> 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