5

I am new to python and am trying to understand how it handles copies vs references in respect to list unpacking. I have a simple code snippet and am looking for an explanation as to why it is behaving the way it does.

arr = [1, 2, 3, 4]
[one, two, three, four] = arr
print(id(arr[0]), arr[0])
print(id(one), one)
one = 5
print(id(one), one)

The output is:

(16274840, 1)
(16274840, 1)
(16274744, 5)

I am not sure why one is all the sudden moved to a different memory location when I try to modify its contents.

I am using python version 2.7.18.

This is my first post, so I apologize in advance if I am not adhering to the guidelines. Please let me know if I have violated them.

Thank you for all the responses. They have helped me boil down my misunderstanding to this code:

var = 1
print(id(var), var)
var = 5
print(id(var), var)

With output:

(38073752, 1)
(38073656, 5)

Asking about lists and unpacking them was completely obfuscatory.

This does a great job of explaining: http://web.stanford.edu/class/archive/cs/cs106a/cs106a.1212/handouts/mutation.html

freedom35
  • 53
  • 4
  • I think becuase the value `1` is still stored at the other location for the variable `arr` and it needs to create the value `5` for the varaible `one` – Anthony L Aug 21 '22 at 00:24
  • You are _not_ modifying the contents of `one`; you are creating a _whole new value_ which is then bound to the same name. – John Gordon Aug 21 '22 at 00:24

2 Answers2

6

The id/address is not associated with the variable/name; it's associated with the data that the variable is referring to.

The 1 object is, in this instance, at address 16274840, and the 5 object is at address 16274744. one = 5 causes one to now refer to the 5 object which is at location 16274744.


Just to rephrase this in terms of C, I think your question essentially boils down to "why does the following not modify the first element of arr?" (I'm ignoring unpacking since it isn't actually relevant to the question):

arr = [1, 2, 3, 4]
one = arr[0]
one = 5

I would approximate that code to the following C which also does not modify arr:

int internedFive = 5;

int arr[4] = {1, 2, 3, 4};

int* one = &arr[0];
one = &internedFive;

printf("%d", arr[0]);  // Prints 1

one originally pointed to the first element of arr, but was reassigned to point to the 5. This reassignment of the pointer has no effect on the data location originally pointed to by one and arr[0].

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
  • Thank you. It seems I have a fundamental misunderstanding of the language. Is there any way I can modify the contents of a list after unpacking? – freedom35 Aug 21 '22 at 00:27
  • 1
    @annoyed_person: Not via the names unpacked to, unless the object(s) in question are themselves mutable (`int`s are not). For all practical purposes, with immutable objects, there is no association between the `list` and what you unpacked to from there on out (they happen to bind to the same object, but you can't change one binding from the other). Do you know any non-Python languages? You can think of Python names as being like C pointers, where any use of them besides direct assignment automatically dereferences. – ShadowRanger Aug 21 '22 at 00:32
  • 1
    @annoyed_person No, you would need to modify `arr` directly. If the contents of `arr` were mutable, you could mutate them directly, but `=` just changes *what* object is being referred to. It doesn't change the object, so you won't see the "change" at other places that the object is referenced (like inside of the list). If you know C, think of Python variables like self-dereferencing pointers, and it will make a lot more sense. – Carcigenicate Aug 21 '22 at 00:35
  • @ShadowRanger: yes, I am most comfortable with C, which is why I like to think of modifying contents at a memory location. I appreciate all the responses, but I think I need some time to wrap my head around python's immutable object paradigm. – freedom35 Aug 21 '22 at 00:39
  • 1
    @freedom35 See my edit. I tried to rephrase the answer in terms of C. – Carcigenicate Aug 21 '22 at 01:02
  • 2
    There isn't an "immutable object paradigm". It's a *reference semantics* paradigm (which is the same as how Java treats non-primitive types, and how C# normally treats `class` types, for example). It's not that certain objects are special because there isn't a way to mutate them; the key point is that *mutating an object is fundamentally different from reassigning a name*. – Karl Knechtel Aug 22 '22 at 01:35
  • @KarlKnechtel: Good point. I think a lot of people coming from a Java/C# background get tripped up here, because they have fundamentally different rules for primitives (and in the case of C#, structs), while Python uses the reference semantics for *everything*, which can feel weird for "primitives" like `int` (that are not special primitives at all in Python). – ShadowRanger Aug 22 '22 at 16:56
5

one is not an object. It's a name that binds to an object. When you reassign one = 5, you rebind it from the object containing the value 1, to a different object containing the value 5, but the only thing one stores is the updated reference, not the value itself.

To be clear, you can't "modify the contents" of an int; ints are immutable. If you want an example where you can modify the contents of an object without modifying the address, consider:

lst = [0]
print(id(lst), lst)
lst[0] = 1
print(id(lst), lst)

which leaves lst bound to the same list the whole time, only changing the contents of said list.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Thank you. My goal is to update a list after unpacking it. Is there a way to modify the object at 16274840 after unpacking it? Maybe I should go back to C... – freedom35 Aug 21 '22 at 00:32
  • 1
    @freedom35: Short answer: Nope. Long answer: Not without inadvertently breaking the language; CPython uses singletons for small `int`s, so if you used `ctypes` hacks to change the value at `16274840` from `1` to `5`, you'd change *every* `1` in the program to have the value `5`, which would do truly awful things. `int` objects are immutable; you are not able to change their contents under normal circumstances, and doing so via `ctypes` or custom C extensions violates language rules in terrible, terrible ways. – ShadowRanger Aug 21 '22 at 00:37
  • Makes sense when you put it that way. Another follow up is, in my original code, can I associate the variable name `arr[0]` and the variable name `one` such that changing the location bound to `one` also changes the location bound to `arr[0]`? – freedom35 Aug 21 '22 at 00:58
  • 1
    @freedom35: Nope. Python has no such capability in the language. – ShadowRanger Aug 21 '22 at 01:31