简体   繁体   中英

List pass by reference confusion in Python

I have this snippet of code that just sorts a list of numbers that are guaranteed to be between 0 and R-1 (inclusive). The following code does the sort correctly but I don't understand why the input passed in remains unmodified.

def bucket(arr, R):
    assert type(arr) is list
    for i in arr:
        assert i >=0 and i < R
    b = [0] * R
    for i in arr:
        b[i]+=1
    arr = []
    for ind, v in enumerate(b):
        arr = arr + [ind] * v
    print(arr)

Why is inp in this example unchanged after the function has been called:

>>> inp
[3, 1, 4, 5, 4, 5, 5, 5, 1, 5]
>>> bucket(inp, 8)
[1, 1, 3, 4, 4, 5, 5, 5, 5, 5]
>>> inp # unchanged, why?
[3, 1, 4, 5, 4, 5, 5, 5, 1, 5]

Because you create a new variable called arr in the line arr = [] and from this point on you operate on a new list. Similarly you always create new lists inside the following for -loop with the arr = arr + [ind] * v operations.

You could simply change it to:

def bucket(arr, R):
    assert type(arr) is list
    for i in arr:
        assert i >= 0 and i < R
    b = [0] * R
    for i in arr:
        b[i] += 1
    arr[:] = []  # remove all items from the list (in-place)
    for ind, v in enumerate(b):
        arr.extend([ind] * v)  # extend the list in-place, you could also use "arr += [ind] * v"
    print(arr)

Example:

>>> inp = [3, 1, 4, 5, 4, 5, 5, 5, 1, 5]
>>> bucket(inp, 8)
[1, 1, 3, 4, 4, 5, 5, 5, 5, 5]
>>> inp
[1, 1, 3, 4, 4, 5, 5, 5, 5, 5]

By assigning [] to arr you are losing the reference to the existing array, and creating a new one.

To change it, you could use

inp.sort()

More info on sort vs sorted

Python passes by assignment, and the semantics are extremely similar to Java's pass by value.

The confusion arises because you are passing the pointer by value. This means you cannot modify the pointer inside the function, but no one can stop you from modifying what the pointer is pointing to (ie the data) So, for example:

x = 3
def change_x(x):
    x = 5
    return x
change_x(x)
print x
# Outputs 3

The value of x outside the function (in this case, 3) was copied before x entered the function, so the assignment does nothing. This is typically what we expect when we hear pass by value.

x = [1,2,3]
print(id(x))
# Outputs some "address"
def change_x(x):
    x.clear()
    x.extend([4,5,6])
    print(id(x))
    # Outputs the SAME address as above
    return x
change_x(x)
print(x)
# Outputs [4,5,6]

What the heck. I thought we just said we couldn't change x. The catch is we didn't change x! We just changed the data x is pointing to (there is a subtle, but important difference here). x is still the same pointer. Note that the addresses output by id are the same.

x = [1,2,3]
print(id(x))
# Outputs some "address"
def change_x(x):
    x = [4,5,6]
    print(id(x))
    # Outputs some DIFFERENT "address". The pointer to x was changed (inside this function).
    return x
change_x(x)
print(x)
# Outputs [1,2,3]

It copied the value of x before it was passed in (the pointer value, not the data, was copied). So, again, reassignment does nothing. Note that the id function outputs different values this time. So after the function returns, we get the original value of x (the original value of the pointer, that is)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM