14

Why does the variable L gets manipulated in the sorting(L) function call? In other languages, a copy of L would be passed through to sorting() as a copy so that any changes to x would not change the original variable?

def sorting(x):
    A = x #Passed by reference?
    A.sort() 

def testScope(): 
    L = [5,4,3,2,1]
    sorting(L) #Passed by reference?
    return L

>>> print testScope()

>>> [1, 2, 3, 4, 5]
Ryan
  • 2,650
  • 3
  • 29
  • 43
  • I understand that sort() mutates the collection but why is it mutating the original L that was defined in testScope(). It sounds like sorting(L) is passing the reference of L to the sorting function? Right? Is that a correct statement? – Ryan Mar 08 '11 at 15:07
  • @lunixbochs: No, no, no! PLEASE stop implying languages like Python, Java, C# (when using reference types), etc. use pass-by-reference. It's plain wrong. –  Mar 08 '11 at 15:21

4 Answers4

21

Long story short: Python uses pass-by-value, but the things that are passed by value are references. The actual objects have 0 to infinity references pointing at them, and for purposes of mutating that object, it doesn't matter who you are and how you got a reference to the object.

Going through your example step by step:

  • L = [...] creates a list object somewhere in memory, the local variable L stores a reference to that object.
  • sorting (strictly speaking, the callable object pointed to be the global name sorting) gets called with a copy of the reference stored by L, and stores it in a local called x.
  • The method sort of the object pointed to by the reference contained in x is invoked. It gets a reference to the object (in the self parameter) as well. It somehow mutates that object (the object, not some reference to the object, which is merely more than a memory address).
  • Now, since references were copied, but not the object the references point to, all the other references we discussed still point to the same object. The one object that was modified "in-place".
  • testScope then returns another reference to that list object.
  • print uses it to request a string representation (calls the __str__ method) and outputs it. Since it's still the same object, of course it's printing the sorted list.

So whenever you pass an object anywhere, you share it with whoever recives it. Functions can (but usually won't) mutate the objects (pointed to by the references) they are passed, from calling mutating methods to assigning members. Note though that assigning a member is different from assigning a plain ol' name - which merely means mutating your local scope, not any of the caller's objects. So you can't mutate the caller's locals (this is why it's not pass-by-reference).

Further reading: A discussion on effbot.org why it's not pass-by-reference and not what most people would call pass-by-value.

  • only *mutable* objects can be mutated, not all. A string is [immutable](https://docs.python.org/2/glossary.html). You can only change a variable [name's binding](https://docs.python.org/2/reference/executionmodel.html#naming-and-binding) so the name refers to a different object, but you can't change an immutable object itself. Immutable objects have no such methods that would make it possible to change their value. A list is called a *mutable* object which means it has methods or operations that would change it's value. *Name binding* is a completely separate thing from *mutability* in Python. – n611x007 May 15 '14 at 16:29
  • @naxa What, exactly, is your point regarding this answer? Binding names is indeed completely separate from mutating objects, and the answer says that too. The reason it mention both is *to point out that they are different*. The reason for pointing that out is that a virtually all beginner confusion about argument passing and mutability comes from them not being aware of this distinction. –  May 15 '14 at 16:36
  • sorry! the point is: you have an incorrect or misleading sentence here: `The actual objects have 0 to infinity references and everyone with a reference can mutate them.` because you are *not* able to mutate an object only by having a reference to it. It needs to *be* mutable. I think making beginners understand is more important than correctness, but I *also* think that giving false assumptions is an equally bad idea. The thing is hard to fix. But it is better fixed. If I have alternatives I should let you know. Off: are you ok with removing our prev comments to keep it uncluttered? – n611x007 May 15 '14 at 18:01
  • @naxa I see. But I can't think of a better way to put that. One could say "attempt to mutate", but that raises the question of whether it might fail depending on where the reference came from. One could exempt immutable objects as a special case, but that seems to go down the nonsensical "immutable objects are treated differently by the language" road. I'm inclined to leave it and treat it as vacuous truth: All code that mutates immutable objects (i.e. none) can do so regardless of where it got its reference to that object. I think that's less misleading than all alternatives I can think of. –  May 15 '14 at 18:09
  • @naxa Actually I think I found a better phrasing, check it out. –  May 15 '14 at 18:11
  • I agree, it keeps it simple but is actually correct. Nice find! – n611x007 May 15 '14 at 18:18
11

Python has the concept of Mutable and Immutable objects. An object like a string or integer is immutable - every change you make creates a new string or integer.

Lists are mutable and can be manipulated in place. See below.

a = [1, 2, 3]
b = [1, 2, 3]
c = a

print a is b, a is c
# False True

print a, b, c
# [1, 2, 3] [1, 2, 3] [1, 2, 3]
a.reverse()
print a, b, c
# [3, 2, 1] [1, 2, 3] [3, 2, 1]
print a is b, a is c
# False True

Note how c was reversed, because c "is" a. There are many ways to copy a list to a new object in memory. An easy method is to slice: c = a[:]

lunixbochs
  • 21,757
  • 2
  • 39
  • 47
1

It's specifically mentioned in the documentation the .sort() function mutates the collection. If you want to iterate over a sorted collection use sorted(L) instead. This provides a generator instead of just sorting the list.

Exelian
  • 5,749
  • 1
  • 30
  • 49
0
a = 1
b = a
a = 2
print b

References are not the same as separate objects.

.sort() also mutates the collection.

Jakob Bowyer
  • 33,878
  • 8
  • 76
  • 91
  • So, you're saying that the object in memory "1" still exists and is still pointed to by the variable b? – Ryan Mar 08 '11 at 22:12