更改的深层副本时,原始列表的元素会更改

时间:2016-01-02 23:30:51

标签: python python-2.7

我有一个全局列表(列表)变量。我将全局列表的浅表副本作为参数发送给另一个函数。

令人惊讶的是,当我从调用函数中的参数中删除一些元素时,原始列表会发生变化。

有人可以告诉我它为什么会发生以及如何防止这种情况发生吗?

以下是简化的代码示例:

def saveCandidateRoutes(candidateRoutes):

        for route in candidateRoutes:
            if route:       # check if the list, 'route', is empty. 
                tweetId = route.pop(0)
                stanox = route.pop(-1)
                ....

def main():

        global allCandidatePaths

        copyOfAllCandidatePaths= list(allCandidatePaths) # making a deep copy 
        result = saveCandidateRoutes(copyOfAllCandidatePaths)

4 个答案:

答案 0 :(得分:2)

python使用对象的引用,这意味着allCandidatePathscandidateRoutes都指向内存中的相同列表,您可以使用它们来更改列表。

为了防止这种情况发生,请在函数saveCandidateRoutes的开头添加此指令candidateRoutes = list(candidateRoutes)list()函数将在内存中创建原始列表的另一个副本,并将其引用分配给candidateRoutes

因此,当您使用candidateRoutes时,您将不会处理主函数中的原始列表,但您将处理另一个列表。

答案 1 :(得分:1)

我认为你对这些条款感到困惑。

浅拷贝是复制的类型,其中复制列表的元素仍然与相同的内存值绑定原始列表的元素

您正在寻找的是深度复印 Here is a good source to find out.

还有: Wikipedia

答案 2 :(得分:1)

我认为您需要快速提醒浅层和深层副本,以及如何制作深层副本。

>>> a = [[1,2], [3,4]] # a list of mutable elements
>>> b = a[:]
>>> c = list(a)

bc都是a的浅层副本,您可以检查abc是不同的对象,因为他们不共享id

>>> id(a)
140714873892592
>>> id(b)
140714810215672
>>> id(c)
140714873954744

但是,abc的每个元素仍然是我们在定义时创建的列表[1,2][3,4]的引用。当我们改变列表中的项目时,这一点就变得清晰了:

>>> c[1][1] = 42
>>> a
[[1, 2], [3, 42]]
>>> b
[[1, 2], [3, 42]]
>>> c
[[1, 2], [3, 42]]

如您所见,第二个列表的第二个元素在abc中发生了变化。 现在,要制作a的深层副本,您有几种选择。一个是列表理解,您可以复制每个子列表:

>>> d = [sublist[:] for sublist in a]
>>> d
[[1, 2], [3, 42]]
>>> d[1][1] = 23
>>> d
[[1, 2], [3, 23]]
>>> a
[[1, 2], [3, 42]]

如您所见,a中的42未更改为23,因为ad中的第二个列表是不同的对象:

>>> id(a[1])
140714873800968
>>> id(d[1])
140714810230904

创建深层副本的另一种方法是使用copy.deepcopy

>>> from copy import deepcopy
>>> e = deepcopy(a)
>>> e
[[1, 2], [3, 42]]
>>> e[1][1] = 777
>>> e
[[1, 2], [3, 777]]
>>> a
[[1, 2], [3, 42]]

答案 3 :(得分:0)

浅拷贝复制对象但不复制任何属性。对于列表,这意味着它的元素被分配给新列表的元素。如果元素是整数,则会得到一个全新的列表,因为int是基本类型*,因此不需要复制它。如果元素是列表,就像在二维列表中那样,它们会被分配给副本,因此每个元素都有两个引用,每个元素一个。如果要复制内部列表的元素,则需要深层复制,以递归方式复制每个对象的属性(在本例中为元素)。

*原始类型与非原始类型之间实际上没有区别,但这相当于它在本解释范围内的工作方式。