6

我们来看一个简单的代码:

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' 保持不变。我已经了解到这是因为一个是可变的,另一个不是。但是如何更改代码以使函数不更改列表?

例如做这样的事情(为简单起见,用伪代码):

a = [1,2,3,...,n]

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

b = doSomething(a)

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

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

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,但这些值也是列表,因此对于列表中的每个列表,操作必须加倍。如何使用嵌套列表执行此操作?

4

7 回答 7

18

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

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

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

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

于 2013-07-16T20:47:18.300 回答
6

创建列表的副本。使用testList = inputList[:]. 查看代码

>>> 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]

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

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

正如其他人指出的那样,您也可以使用理解。

>>> def plusOne(y):
        return [elem+1 for elem in y]
于 2013-07-16T20:45:10.343 回答
4

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

print plusOne(y[:]), y

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

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

并继续工作y_copy


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

return [x + 1 for x in y]
于 2013-07-16T20:45:19.677 回答
3

只需创建一个包含您想要的值的新列表并返回它。

def plus_one(sequence):
    return [el + 1 for el in sequence]
于 2013-07-16T20:50:36.960 回答
2

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

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

但是,您可以通过列表理解获得所需的输出

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

这将遍历其中的值并通过向每个值添加一个来y创建一个新列表

于 2013-07-16T20:52:22.603 回答
2

要回答您编辑的问题:

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

于 2013-07-17T21:21:11.643 回答
1

您可以通过创建一个函数并通过 map function 调用此函数来做到这一点,map function 将调用 add 函数并为其提供值,然后它将像这样打印新值:

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

或者你可以使用 (*range) 函数,就像那个例子一样很容易做到这一点:

print ([i+i for i in [*range (1,10)]])
于 2019-02-05T12:31:26.557 回答