Search code examples
pythonlistreferencecopy

I'm curious about python list's initialization and reference


Let me make a list and change one element like this:

list = [float("inf")] * 3
list[2] = 0

then I print it, It prints

print(list)
> [inf, inf, 0]

I thought each element references the same one, but it's not.

However, when I make a second-dimension list and change one element like this:

list = [[float("inf")] * 3] * 3
list[2][2] = 0

then I print it, it prints

print(list)
> [[inf, inf, 0], [inf, inf, 0], [inf, inf, 0]]

then I am in chaos. in the first dimension, changing one element reflects to one element. but in second dimension, all rows reflect the change. how can it be happen?

I thought the second result should be:

print(list)
> [[inf, inf, inf], [inf, inf, inf], [inf, inf, 0]]

and according to the second result, shouldn't the first result be like this?

print(list)
> [0, 0, 0]

please, help me. I can't understand it.


Solution

  • You're misunderstanding what happens in the first example:

    nums = [float("inf")] * 3
    nums[2] = 0
    print(nums)
    # [inf, inf, 0]
    

    After the first line is executed, all of the list elements do reference the same object. You can see this by printing all their ids and seeing that they're the same:

    >>> nums = [float("inf")] * 3
    >>> for n in nums:
    ...     print(id(n))
    ...
    2996959434960
    2996959434960
    2996959434960
    

    The part you misinterpreted is what happens when you do nums[2] = 0. This does not mutate one of the infs (because they are in fact all the same object, which is itself immutable). Assigning something to an object's name never modifies the object itself, only the name.

    So subscript assignment mutates the list so that it's 2-th index (i.e. one of the 3 "names" for that one inf object) now references a different object:

    >>> nums[2] = 0
    >>> for n in nums:
    ...     print(id(n))
    ...
    2996959434960
    2996959434960
    2996958396624
    

    When you have a list of references to the same list, and you mutate the inner list, the top-level list is unchanged, so it sees the inner list change across all three of its "names".

    It may help to think of:

    nums = [float("inf")] * 3
    

    as being equivalent to:

    n = float("inf")
    num0, num1, num2 = n, n, n
    

    All three num variables point to the same n. When you reassign one of them:

    num2 = 0
    

    you reassign that variable, but you're not changing n or any of the other nums.

    The same exact logic applies if you do:

    nums = [float("inf")] * 3
    arr0, arr1, arr2 = nums, nums, nums