5

Consider the following code, at first glance it does the same thing, but the result is different, sometimes it seems list is pass by value, sometimes list seems pass by reference:

lst = [1, 2]
def f(lst):
#     lst = lst + [3]  # seems pass by value
#     lst += [3]  # strange! same as above but seems pass by reference
    lst = lst.append(3)  # seems pass by reference
    return lst
f(lst)  
print(lst)

can anyone tell me what is going on?

an offer can't refuse
  • 4,245
  • 5
  • 30
  • 50
  • 2
    The difference is in assignment, not passing semantics. – MisterMiyagi Nov 24 '18 at 14:57
  • 1
    See https://stackoverflow.com/questions/575196 – FMc Nov 24 '18 at 14:57
  • I think you have all the links by now. I just suggest the print of `id(name)` in several places when you have doubts like that. – progmatico Nov 24 '18 at 15:17
  • Python doesn't have the concepts of references and pointers. The way it works is that _mutable_ objects (like `list`s and `dict`s) get passed as references and all others are by value. – martineau Nov 24 '18 at 15:40
  • 1
    @martineau that is not true. The evaluation strategy is exactly the same for any type of object. – juanpa.arrivillaga Nov 24 '18 at 16:05
  • The answer is that Python uses neither evaluation strategy, it uses [call by sharing](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing), which is the exact same evaluation strategy used by Java, Ruby, Javascript etc. The other thing is that in some cases you are using mutator methods, in others you are not, so the list concatenation operator `+` *always* creates and returns a copy, whereas augmented assignment `+=` modifies in-place, and so does `.append` – juanpa.arrivillaga Nov 24 '18 at 16:09
  • @martineau: A Python name is a reference. When passing a name into a function, a new name in the local function's scope is created with the formal parameter name, pointing to the same object as the function call argument. I have seen it called "by shared reference". It does not copy the object. And it always happens like that. this is different from C that always makes a copy of the value or a copy of the reference. Then (in Python) the object can be mutable or immutable. Mutables can be changed in-place. But assignment always creattes a new object. – progmatico Nov 24 '18 at 16:13
  • @progmatico erm, assignment *never* creates a new object. `a = []; b = a; b.append('foo'); print(a)` – juanpa.arrivillaga Nov 24 '18 at 16:14
  • By the way @juanpa.arrivillaga is there a canonical name for this, everyone looks to use a slightly different name for this. – progmatico Nov 24 '18 at 16:15
  • @progmatico well Barbara Liskov first described it for its use CLU as "call by sharing" but that never really took off. It has different names in different communities. I think "call by assignment" is probably a good one, but I've given up hope on there ever being any agreement across communities – juanpa.arrivillaga Nov 24 '18 at 16:16
  • Crucial reading: https://nedbatchelder.com/text/names.html to understand Python's relatively simple semantics, just read that link, and don't worry about "call by value" vs "call by reference" – juanpa.arrivillaga Nov 24 '18 at 16:18
  • Ok. I meant assignment to the previous used name. – progmatico Nov 24 '18 at 16:19
  • I meant assignment to a parameter name inside the function rebinds it to different object on the right, and you loose access to the original object, at least using that name, if you haven't kept another reference to it. "But assignment always creates a new object" is quite wrong. But I never thought such a thing. – progmatico Nov 24 '18 at 17:14
  • I was thinking it binds to a different local object. I think I come to wrote it that way because of when you assign an immutable while thinking that you changed the original (hence the different value, and the possibly new object). – progmatico Nov 24 '18 at 17:14
  • The assign does not "create" anything (besides the name itself if not re-used), and actually you don't even know if the immutable on the right side is new or a re-used object of same value. But "Always binds to a different object" is definitely better. Note that this not consider augmented assignment of mutables, of course. – progmatico Nov 24 '18 at 17:14
  • 1
    I am also sure @martineau knows it better than his own words... – progmatico Nov 24 '18 at 17:18

1 Answers1

9

It’s passed by value of reference. So modifications to the object can be seen outside the function, but assigning the variable to a new object does not change anything outside the function.

It’s essentially the same as passing a pointer in C, or a reference type in Java.

The result of the += case is because that operator actually modifies the list in place, so the effect is visible outside the function. lst.append() is also an in-place operation, which explains your last case.

arshajii
  • 127,459
  • 24
  • 238
  • 287