5

According to this tutorial, Python uses "Pass By Reference."

They then go on to give the following example. On what planet is this "Pass By Reference"? It looks like a clear cut case of "Pass By Value" to me.

Thoughts?

def changeme( mylist ):
   mylist = [1,2,3,4];
   print "Values inside the function: ", mylist
   return

mylist = [10,20,30];
changeme( mylist );
print "Values outside the function: ", mylist

The parameter mylist is local to the function changeme. Changing mylist within the function does not affect mylist. The function accomplishes nothing and finally this would produce the following result:

# Values inside the function:  [1, 2, 3, 4]
# Values outside the function:  [10, 20, 30]

Update, Conclusion:

  1. per answer by viraptor below, "according to the docs, python is pass-by-value". So the tutorial is clearly wrong.

  2. Python is not "call by reference", it is "call by sharing". This means a reference can be passed to a local subroutine, updated by the subroutine, and the update stays local to the subroutine and doesn't automatically affect the global value of the reference.

thanks_in_advance
  • 2,603
  • 6
  • 28
  • 44
  • If you do in-place operations to `mylist` within the function, the changes persist outside the function. The assignment statement is not an in-place operation on the existing object, it's creating a new object. – Marius Apr 25 '16 at 23:44
  • Possible duplicate of [How do I pass a variable by reference?](http://stackoverflow.com/questions/986006/how-do-i-pass-a-variable-by-reference) – VHarisop Apr 25 '16 at 23:46
  • `mylist = [1,2,3,4]` rebinds the local `mylist` variable to the `[1,2,3,4]` object. This removes the reference to its previous value. – tdelaney Apr 25 '16 at 23:51
  • 1
    @tdelaney: OP knows this, this is why the question is being asked. In a true pass-by-reference, the original variable binding would have been rebound too. – Amadan Apr 25 '16 at 23:52
  • 6
    Thanks, OP. I'll add that to the list of Python tutorials I'll never recommend. – Robᵩ Apr 25 '16 at 23:54
  • Possible duplicate of [how to assign variable by reference in python?](http://stackoverflow.com/questions/25640135/how-to-assign-variable-by-reference-in-python) – Rick Apr 26 '16 at 01:03
  • I think the phrase "pass by object reference" makes the most sense. You'll want to read this article. http://robertheaton.com/2014/02/09/pythons-pass-by-object-reference-as-explained-by-philip-k-dick/ – Rick Apr 26 '16 at 01:06

7 Answers7

5

It is neither. It is call by sharing. I've also heard the term "pass by reference value" used.

Also known as "call by object" or "call by object-sharing," call by sharing is an evaluation strategy first named by Barbara Liskov et al. for the language CLU in 1974. It is used by languages such as Python, Iota, Java (for object references), Ruby, JavaScript, Scheme, OCaml, AppleScript, and many others. However, the term "call by sharing" is not in common use; the terminology is inconsistent across different sources. For example, in the Java community, they say that Java is call-by-value, whereas in the Ruby community, they say that Ruby is call-by-reference, even though the two languages exhibit the same semantics. Call by sharing implies that values in the language are based on objects rather than primitive types, i.e. that all values are "boxed".

The semantics of call by sharing differ from call by reference in that assignments to function arguments within the function aren't visible to the caller (unlike by reference semantics), so e.g. if a variable was passed, it is not possible to simulate an assignment on that variable in the caller's scope. However, since the function has access to the same object as the caller (no copy is made), mutations to those objects, if the objects are mutable, within the function are visible to the caller, which may appear to differ from call by value semantics. Mutations of a mutable object within the function are visible to the caller because the object is not copied or cloned — it is shared.

Community
  • 1
  • 1
Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Didn't actually know what this was called, thought it was just a type of pass-by-reference, big +1 for this! – Serdalis Apr 26 '16 at 00:02
  • 5
    @Serdalis: Pretty much no-one knows what this is called, that's why we're in this mess in the first place. :P Most of the curriculums and tutorials kind of still cling to the binary distinction, and so people feel the need to assign one of those two labels even when neither fits. – Amadan Apr 26 '16 at 00:03
  • 1
    These terms are so muddy ... Even in really well established languages like `C`, you'll hear people talk about pass by reference and pass by value when in fact, `C` is always pass by value. "pass by reference" is simulated by passing a pointer (the reference) by value... – mgilson Apr 26 '16 at 00:06
  • "It is neither." It is pass-by-value. Nobody disputes that the semantics of passing and assigning in Python is exactly the same in Java, which is always described, on this site and elsewhere on the Internet, as pass-by-value. – user102008 Apr 29 '16 at 02:11
  • It really should be called pass-by-value. This is the more standard term, see my answer below – Matt Messersmith Jul 28 '16 at 03:29
  • @mwm314: I saw your answer, and I still disagree. What you call "value" and what Python calls "value" are different things: ["Objects are Python’s abstraction for data. [...\] Every object has an identity, a type and a **value**"](https://docs.python.org/3/reference/datamodel.html). If you want to say "pass-by-value but the value is not Python's value but Python's identity", that's accurate, but hardly clear, which makes a new term useful (whether pass-by-reference-value or pass-by-sharing or something else). But just "pass-by-value", no. – Amadan Jul 28 '16 at 06:48
  • @Amadan: The word value is overloaded. Pass-by-value is a concept that exists outside of Python, it's language agnostic. It is unfortunate and confusing that objects in Python have a well-defined value, and that this is not the same value in the phrase pass-by-value. – Matt Messersmith Jul 28 '16 at 12:40
4

It is pass-by-value, where all the values are pointers to objects. You might think this would imply that you can use the passed-in pointer to change the caller's variable, making it pass-by-reference, but you can't, so it's not.

Key to understanding how Python value-passing works is knowing that there are no "unboxed" (non-object) values. Integers are objects, and a variable that "contains" an integer is really a pointer to an integer object stored somewhere other than the variable. Floats, Booleans, ditto.

Variables don't really "hold" values in Python in the sense that they do in, say, C. And so assignment always consists of making the variable name point to a different object.

If an object passed into a function is mutable, the function can change it, but this must be done entirely without changing what object its name(s) point to. For example:

some_digits_of_pi = [3, 1, 4, 1, 5, 9, 2, 7]

def extend_pi(x):
    x[-1] = 6
    x += [5, 3, 5, 9]

Here we are mutating x inside the function. (For a list, += is essentially list.extend.) Since x is never changed to point to a different object, the changes are made to the list that was passed in. The name some_digits_of_pi refers to the same object as is being modified in the function, so the caller will see that their list of that name has been changed.

If we write x = [2, 7, 1, 8, 2, 8, 1, 8] at the end of the function, that would create a new list object and point the local name x at it. It doesn't change what the caller's variable points to, so that list is not changed by that statement.

In other words, you can't make the caller's variable (some_digits_of_pi in this case) point to a different object. If you change x to point to a different object inside the function, only x points to that object.

Numbers, strings, tuples, and the like work exactly the same way. A pointer to the object is passed in; if you change the value of the argument inside the function, it is made to point to a different object, and this naturally does not change the caller's variable. It only seems different because these types of objects aren't mutable, and there isn't any way to change them in place.

An additional point of confusion is that it looks like ints and lists both have += operators, but it happens that += on an int does something very different from what the same operation does on a list. On a list, += returns the same list object (modified), while on an integer, it may return a completely different object (since integers are immutable).

kindall
  • 178,883
  • 35
  • 278
  • 309
  • I like the explanation, but: "In other words, you can't make the caller's variable point to a different object." - this is what pass-by-reference would mean. I don't think "Or it is pass-by-reference, but you can't use the reference to change the caller's variable." makes sense. We already have a name for it - it's pass-by-value and your argument is a pointer/reference. – viraptor Apr 26 '16 at 00:20
  • Reworded a bit of the intro. – kindall Apr 26 '16 at 00:24
  • Yes, this is correct. I've expanded upon it in my answer for extra clarity. I think this should be the accepted answer!!! – Matt Messersmith Jul 28 '16 at 03:30
2

The naming of the calls is different depending on who you talk to. One thing it's definitely not, is pass-by-reference though.

Both technically and according to the docs, python is pass-by-value.

[...] Otherwise, the value of the argument is placed in the slot, filling it (even if the expression is None, it fills the slot). When all arguments have been processed, the slots that are still unfilled are filled with the corresponding default value from the function definition. [...]

Now, just because the values are actually references, that causes people to argue about the convention and define new names to describe what's going on from a more practical point of view. Call by value reference, by sharing, by handle, etc. are all different names people could use.

viraptor
  • 33,322
  • 10
  • 107
  • 191
  • 1
    Technically, if you tell me Python is pass-by-value, I would have expected an argument to be deep-cloned, like a struct would be in C. Saying "it's pass by value but the value is a reference" is making things less clear for students than if a new name was used. It makes as much sense as saying that "soyburger is technically meat, just not coming from an animal". – Amadan Apr 26 '16 at 00:24
  • 1
    structs are value-types though. Something like that doesn't exist in Python, so you can't really make a valid comparison here. (also structs are just copied, not deep-cloned) This is what I mean about a practical and a technical description. Python passes by value whether it's clear or confusing for students. If other names make it more clear, great. But it's still worth remembering what pass-by-value actually means. (and no, soyburger is not technically meat because there's no meat in soy; technically means "Based on precise facts", not "it's fun to describe it that way") – viraptor Apr 26 '16 at 00:35
  • @Amadan: "I would have expected an argument to be deep-cloned, like a struct would be in C." You're comparing apples and oranges. Here in Python all values (including variables and parameters) are pointers; and the semantics is exactly the same as if all variables were of pointer type in C. Just because the types are not written out in Python does not change that. You say struct types in C but that does not exist in Python; just because Python lacks a certain type of types doesn't change its passing semantics. – newacct Apr 29 '16 at 02:14
2

I never knew the name for it, but according to Amadan, it is "call by sharing". I'll make an analogy to make it easier to understand.

Let's say I have a dog and I name him "Brutus". I take Brutus for a walk and meet my neighbor Sam. Sam doesn't like the name I chose and calls my dog "Dodo". If he puts a knife into Dodo, it of course effects Brutus because they are the same dog. Let's say he doesn't and Billy, another neighbor, is out walking with his dog. Sam changes his mind and decides that Billy's dog is Dodo. If he puts a knife into Dodo, it doesn't effect Brutus because they are different dogs.

Your function is similar. mylist walks over to your function and your function decides to call him mylist, the same name. Your function then decides that [1,2,3,4] is mylist. You have now discarded the original and defined a new list. Your reassignment does not effect the original list.

zondo
  • 19,901
  • 8
  • 44
  • 83
1

mylist = [1,2,3,4] is created inside of changeme() mylist is a local variable, it is not the parameter you passed in even it has the same name as the argument. It doesn't refer to parameter mylist

Haifeng Zhang
  • 30,077
  • 19
  • 81
  • 125
0

As per @Amadan's answer Python uses Call by Sharing.

Within the function you have re-assigned the reference to a new list so it is no longer pointing to the list you passed in.

You can see this happening with the following code:

def changeme( mylist ):
   print locals()  # will show mylist value in function.
   mylist = [1,2,3,4];
   print locals()  # will show mylist value in function.
   print globals()['mylist'] # will show global mylist value
   print "Values inside the function: ", mylist
   return

Where you will see the value of mylist change after the assignment in the locals list, but the global reference is still the same.

Serdalis
  • 10,296
  • 2
  • 38
  • 58
-1

Python, as far as I'm concerned, is just like Java, in the fact that all functions (methods) are pass-by-value. See this infamous answer. The downfall of "call by sharing" is that it makes you think something different is happening for different types (i.e. mutable types behave differently in function calls than immutable types). This really is not the case, the same thing happens every time you call a function. Also, no textbook I've read mentions the concept, I think pass-by-value and pass-by-reference are much more popular terminology.

How does pass by value work?

It works by copying the "value" which was passed in to the function before modifying it in the function. So, take this example:

my_int = 4
def pass_by_value(value):
    value += 1
    return value
print pass_by_value(my_int)
print my_int

this outputs:

5
4

Observe that the value of my_int didn't change. It stayed at 4 even though the function incremented the value which was passed in to be 5. This is because it was copied. There are several subtleties here which we will return to. Bear with me.

Pass by value on a list

my_list = [1,2,3]
def pass_by_value_list(li):
    li.append(4)
    return li
print pass_by_value_list(my_list)
print my_list

this outputs:

[1, 2, 3, 4]
[1, 2, 3, 4]

What the heck. The value of my_list changed. You JUST SAID ABOVE that pass-by-value copies the value! Therefore, my_list should not have changed!!!

The first example was too subtle. I failed to properly define what "value" was being copied. It turns out, the value that is copied is not the data, but rather, where the data is stored. Typically C/C++ programmers call this a pointer or address. Let's revisit the examples, but modify them a bit to drive home the point.

How does pass by value work? Version 2.0:

my_int = 4
def pass_by_value(value):
    print "Address of parameter before += is: ", id(value)
    value += 1
    print "Address of parameter after += is: ", id(value)
    return value
print "Address of parameter outside of the function is: ", id(my_int)
pass_by_value(my_int)

When I ran this, my output was:

Address of parameter outside of the function is:  40592528
Address of parameter before += is:  40592528
Address of parameter after += is:  40592504

Looks like the address of the parameter before the += operator was 40592528. This is also the same "value" that the parameter outside the function had! But after the += operator, the "value" changed inside the function! However, that change in address was not propagated outside the function, because the address is pass-by-value. The new address inside the function is 40592504 (different from 40592528, albeit close). Long story short, the += operator makes a new int, and does not operate on the data of the address passed into the function.

Pass by value on a list, Version 2.0:

my_list = [1,2,3]
def pass_by_value_list(li):
    print "Address of parameter before .append() is: ", id(li)
    li.append(4)
    print "Address of parameter after .append() is: ", id(li)
    return li
print "Address of parameter outside of the function is: ", id(my_list)
pass_by_value_list(my_list)

this output:

Address of parameter outside of the function is:  110841160
Address of parameter before .append() is:  110841160
Address of parameter after .append() is:  110841160

Hey, that's different than the integer case. Looks like all the addresses are the same! Indeed, this is different from the integer case. The append function operates on a list in place, and does not return a new list. This is why you see a change to my_list outside of the function. append modifies the list at the address which was passed in to the function, so we see the data changed everywhere throughout the program. What didn't change, though, was the address.

Observe that:

my_list = [1,2,3]
def dont_change_the_list(li):
    li.append(4)
    li = []
    return li
print dont_change_the_list(my_list)
print my_list

outputs

[]
[1, 2, 3, 4]

In other words, the change to the list inside the function wasn't propagated outside the function, even though this seemed to be the behavior we saw before. This is because the statement li = [] changes the address of li, but we copied the address before the function executed. The address of the parameter is the "value" which is copied, and is not possible to change inside of a function. So, this did not change the data in my_list in the main program.

The point: Python always uses pass-by-value semantics on its function calls. The value just happens to be the address of the object which was passed in, not the data of the object which was passed in.

Community
  • 1
  • 1
Matt Messersmith
  • 12,939
  • 6
  • 51
  • 52