函数在Python中更改列表值而不是变量值

Function changes list values and not variable values in Python

本问题已经有最佳答案,请猛点这里访问。

让我们用一个简单的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
y = [1,2,3]

def plusOne(y):
    for x in range(len(y)):
        y[x] += 1
    return y

print plusOne(y), y


a = 2

def plusOne2(a):
    a += 1
    return a

print plusOne2(a), a

"y"的值更改,但"a"的值保持不变。我已经知道这是因为一个是可变的,另一个不是。但是如何改变代码,使函数不改变列表呢?

例如,要执行类似的操作(为了简单起见,使用伪代码):

1
2
3
4
5
6
7
8
9
10
a = [1,2,3,...,n]

function doSomething(x):
    do stuff with x
    return x

b = doSomething(a)

if someOperation(a) > someOperation(b):
    do stuff

编辑:对不起,但是我在嵌套列表中还有一个问题。请参阅此代码:

1
2
3
4
5
6
7
8
9
10
11
def change(y):
    yN = y[:]
    for i in range(len(yN)):
        if yN[i][0] == 1:
            yN[i][0] = 0
        else:
            yN[i][0] = 1
    return yN

data1 = [[1],[1],[0],[0]]
data2 = change(data1)

这里不起作用。为什么?再说一次:如何避免这个问题?我理解它为什么不起作用:yn=y[:]将y的值复制到yn,但这些值也是列表,因此对于列表中的每个列表,操作都必须加倍。如何对嵌套列表执行此操作?


python变量包含指向对象的指针或引用。所有值(甚至整数)都是对象,赋值会更改变量以指向不同的对象。它不会在变量中存储新的值,它会更改变量以引用或指向其他对象。由于这个原因,许多人说python没有"变量",它有"名称",并且=操作不"为变量赋值",而是"将名称绑定到对象"。

plusOne中,您正在修改(或"变异")y的内容,但决不更改y本身所指的内容。它仍然指向同一个列表,即传递给函数的列表。全局变量y和局部变量y引用同一列表,因此使用任一变量都可以看到更改。由于您更改了传入对象的内容,实际上没有理由返回y(实际上,返回None)是Python自己对这样修改列表的操作所做的事情,"就地"是指创建新对象而不是改变现有对象的操作返回值。

plusOne2中,您将局部变量a更改为引用不同的整数对象3。(将名称a绑定到对象3)全局变量a不会因此而更改,并继续指向2

如果不想更改传入的列表,请复制并更改。然后,您的函数应该返回新列表,因为它是创建新对象的操作之一,如果不返回新对象,新对象将丢失。您可以这样做作为函数的第一行:例如x = x[:](正如其他人指出的那样)。或者,如果用任何一种方法调用函数都可能很有用,那么如果调用方想要复制一个函数,您可以让它在x[:]中传递。


创建列表的副本。使用testList = inputList[:]。参见代码

1
2
3
4
5
6
7
8
9
>>> def plusOne(y):
        newY = y[:]
        for x in range(len(newY)):
            newY[x] += 1
        return newY

>>> y = [1, 2, 3]
>>> print plusOne(y), y
[2, 3, 4] [1, 2, 3]

或者,您可以在函数中创建一个新列表

1
2
3
4
5
>>> def plusOne(y):
        newList = []
        for elem in y:
            newList.append(elem+1)
        return newList

你也可以像其他人指出的那样使用理解。

1
2
>>> def plusOne(y):
        return [elem+1 for elem in y]


可以使用切片表示法传递列表的副本:

1
print plusOne(y[:]), y

或者更好的方法是在函数本身中创建列表的副本,这样调用方就不必担心可能的修改:

1
2
def plusOne(y):
    y_copy = y[:]

改为研究y_copy


或者正如@abarnet在注释中指出的那样,您可以修改函数以使用list comprehension,这将创建一个新的列表:

1
return [x + 1 for x in y]


只需创建一个包含所需值的新列表,然后返回该列表即可。

1
2
def plus_one(sequence):
    return [el + 1 for el in sequence]

要回答您编辑的问题:

复制嵌套的数据结构称为深度复制。要在python中执行此操作,请在copy模块中使用deepcopy()


正如其他人所指出的,如果不想修改原始列表,您应该使用newlist = original[:]newlist = list(original)来复制列表。

1
2
3
4
5
def plusOne(y):
    y2 = list(y)  # copy the list over, y2 = y[:] also works
    for i, _ in enumerate(y2):
        y2[i] += 1
    return y2

但是,您可以通过列表理解来实现您想要的输出。

1
2
def plusOne(y):
    return [i+1 for i in y]

这将迭代y中的值,并通过向每个值中添加一个值来创建一个新列表。


您可以通过生成一个函数并通过map函数调用这个函数来实现这一点,map函数将调用add函数并给它赋值,之后它将打印新值,如下所示:

1
2
3
def add(x):
    return x+x
print(list(map(add,[1,2,3])))

或者您可以使用(*range)函数,这样很容易做到:

1
print ([i+i for i in [*range (1,10)]])