简体   繁体   English

列出Python中的引用混淆

[英]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). 我有一段代码,只对保证在0到R-1(含)之间的数字列表进行排序。 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在调用函数后没有发生变化:

>>> 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. 因为您在arr = []行中创建了一个名为arr的新变量,并且从这一点开始,您将对新列表进行操作。 Similarly you always create new lists inside the following for -loop with the arr = arr + [ind] * v operations. 同样,你永远是以下中创建新的列表for -loop与arr = arr + [ind] * v操作。

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. 通过将[]分配给arr您将丢失对现有数组的引用,并创建一个新数组。

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. Python通过赋值传递,其语义与Java的值传递极为相似。

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. 函数外部的x值(在本例中为3)在x进入函数之前已被复制,因此赋值不执行任何操作。 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. 我以为我们只是说我们不能改变x。 The catch is we didn't change x! 问题是我们没有改变x! We just changed the data x is pointing to (there is a subtle, but important difference here). 我们只是更改了x指向的数据(此处存在细微但重要的区别)。 x is still the same pointer. x仍然是相同的指针。 Note that the addresses output by id are the same. 请注意,由id输出的地址是相同的。

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). 它在传递x之前复制了x的值(复制了指针值,而不是数据)。 So, again, reassignment does nothing. 因此,再分配也无济于事。 Note that the id function outputs different values this time. 注意,这次id函数输出不同的值。 So after the function returns, we get the original value of x (the original value of the pointer, that is) 因此,在函数返回之后,我们得到x的原始值(即指针的原始值)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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