5

Consider the following instance behavior in Python.

def change(elements):
    elements[0] = 888
    elements = [-3, -1, -2, -3, -4]
    print(elements[0])

numbers = [1, 4, 5]

print(numbers[0])
change(numbers)
print(numbers[0])
print(numbers)

The Python snippet below prints

1
-3, 
888 
[888,4,5]

Shouldn't it print

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

Why would an element in a list by successfully overwritten, but not the entire list object?

4
  • 3
    elements[:] = [-3, -1, -2, -3, -4] would produce your expected output... Commented Jul 13, 2018 at 13:07
  • 2
    Reading this will clear up your misunderstanding: Facts and myths about Python names and values Commented Jul 13, 2018 at 13:08
  • @Chris_Rands: True, but absent an explanation why the OP isn't left with a better understanding of of the language. The key difference is elements = somelist is simple assignment and merely rebinds the local name to a different object, whereas elements[:] = somelist only looks like assignment. It's really syntactic sugar for calling a mutating method on the existing object referred to be the name elements. Commented Jul 13, 2018 at 13:19
  • Thanks guys, these are great comments. Commented Jul 13, 2018 at 14:50

5 Answers 5

2

The list is passed as a reference to the function. So the list is shared between the global space and function. The first change modifies the list and that change is reflected in both spaces. But then you do and assignment to the list variable. That creates a new list in the function. The original list exists in the global space, but it no longer exists in the function. So all of the changes made to the list in the function from that point on are only visible within the function.

Another way of thinking about this instance is that the list is a type of container. The first line of the function changes what is in the container. The second line (the assignment) actually changes the container you are using. This is something that is true for lists but also for any object. This is why the solution given by @Chris_Rands in the comments works: it modifies what is in the container, it doesn't change the container.

Sign up to request clarification or add additional context in comments.

2 Comments

"Pass by reference" carries baggage that isn't quite correct with Python (but it is good enough). "Pass by assignment" has become a popular way of describing it because follows the semantics of Python's assignment.
@StevenRumbalski Thanks for pointing that out. I didn't really think about the distinction between a reference to an object versus a reference to memory, but it makes sense. I learned something new today too! :)
1

That is a classic Python variable naming reference confusion. What I really like to to do to understand that though is print a reference to see what is going on.

def change(elements):
    elements[0] = 888
    print("ID of elements is: %d",id(elements))
    elements = [-3, -1, -2, -3, -4]
    print("ID of elements is: %d",id(elements))
    print(elements[0])

numbers = [1, 4, 5]
print("ID of number is: %d",id(numbers))

print(numbers[0])
change(numbers)
print(numbers[0])
print(numbers)

>>>> ('ID of number is: %d', 140036366181584)
1
('ID of elements is: %d', 140036366181584)
('ID of elements is: %d', 140036366181944)
-3
888
[888, 4, 5]

The ID here represents an emplacement in memory. If you run the code the number might be different, but the behavior stays the same.

Basically, when you call

 elements[0] = 888

You're actually mutating numbers (same ID of 140036366181584 in my example).

But, when you call

elements = [-3, -1, -2, -3, -4]

You're creating a new list (different ID of 140036366181944 in my example) and re-assigning the local name elements to it. Yo're simply assigning the name elements to another object. It's not the same list anymore.

So, at this point all you did to numbers is change its first index with the value 888 and this is what the output shows empirically.

Comments

0

In change function first you use elements from outer scope, that was passed as the argument and is referenced - can be changed within function and those changed will affect the object even out of the local scope.

Then you create local instance of elements, modify it and end the function. Since the elements in global scope were changed earlier, you see "unexpected" result.

Check https://docs.python.org/3/reference/executionmodel.html for more details.

Comments

0

No it shouldn't because in first line you override first value of parameter elements and on the second you define new local variable for function.

Comments

0

Looking at ID's will make understand ...

def change(elements):
    print('inner first elements ID ',id(elements))
    elements[0] = 888
    print('inner second elements ID ',id(elements))
    elements = [-3, -1, -2, -3, -4]
    print('inner third elements ID ',id(elements))
    print('inner third numbers ID ',id(numbers))

numbers = [1, 4, 5]
print(numbers[0])
print('outer numbers ID ',id(numbers))
change(numbers)
print('c ',numbers[0])
print('d', numbers)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.