2

I came across a error in my code and after searching found that the .pop() function changes a list value in a local function, on a global level, without using return function or global statement. I understand what pop does, I've used it before, but I don't understand why it's affecting the value of list globally. Why is that? Is there a name for this and are there other functions that does the same?

#Example code
listname=[1,2,3,4,5]

def checkpop(listname):
    popped = listname.pop()
    listname = ['WTF']
    print (listname)

print(listname)
checkpop(listname)
print(listname)

OUTPUT

[1, 2, 3, 4, 5]

['WTF']

[1, 2, 3, 4]

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Beatrix Kidco
  • 402
  • 5
  • 12
  • 3
    This is expected behavior. It is absolutely not WTF. It is supposed to mutate the list in place. If you want to get the last element of a list use popped = listname[-1] instead. – Vasil Feb 14 '18 at 19:38
  • 1
    There's no such thing as a "local list". There are local variables, but the objects they refer to aren't attached to any variable or scope. – user2357112 Feb 14 '18 at 19:42
  • The *object* is neither local nor global. The *variable* is. The local variables references the same object as the global variable. *Any* modification to that object being referenced locally will still be visible to global references. So all the list mutator methods would work this way, e.g. `list.append`,`list.extend`, `list.clear`, `list.sort`, etc. – juanpa.arrivillaga Feb 14 '18 at 19:44
  • @BeatrixKidco check this excellent blog post on python variables: https://nedbatchelder.com/text/names.html – juanpa.arrivillaga Feb 14 '18 at 19:53

2 Answers2

1

Because assignments do not make any new copy of any object in Python. So, when you pass the global list as the argument to the function, you are binding to the same list object inside the function.

As lists are mutable in Python, when you do a in-place operation in it, you are changing the same global list object.

To get a better grasp, you can always check the id:

In [45]: lst = [1, 2]

In [46]: def foo(lst):
    ...:     print(id(lst))
    ...:     return   

In [47]: id(lst)
Out[47]: 139934470146568

In [48]: foo(lst)
139934470146568
heemayl
  • 39,294
  • 7
  • 70
  • 76
  • Yep, an example is a good idea. You could just do https://ideone.com/DRhDI0 though and use `is` since it does the same thing as comparing `id`s. – Christian Dean Feb 14 '18 at 19:48
1

Your making a false distinction between the object the listname variable refers to and the object the listname parameter refers to.

The listname variable is like a tag. It is simply a reference to the list object [1, 2, 3, 4, 5]. When you passed listname into checkpop, you gave the listname parameter a reference to the same list object the listname variable refers to. The point here, is that the listname variable and the listname parameter point to the same object. Thus, mutating the object the listname parameter refers to, will also change the object that the global listname variable refers to.

Christian Dean
  • 22,138
  • 7
  • 54
  • 87