浅拷贝,为什么列表不会改变

时间:2010-12-25 17:25:15

标签: python copy

我试图理解python中浅拷贝和深拷贝之间的区别。我在这里阅读了很多帖子,他们一直很有帮助。但是,我仍然不太了解这种差异。有人可以解释下面结果的原因。评论中指出了我不理解的结果。

非常感谢。

import copy
import random

class understand_copy(object):
    def __init__(self):
        self.listofvalues = [4, 5]

    def set_listofvalues(self, pos, value):
        self.listofvalues[pos] = value

ins = understand_copy()

newins = copy.copy(ins)

newins.set_listofvalues(1,3)
print "ins = ", ins.listofvalues
print "in newins", newins.listofvalues
# Gives the following output as I would expect based on the explanations.
# prints ins = [4, 3]
# prints newins = [4, 3]


newins.listofvalues.append(5)
print "ins =", ins.listofvalues
print "newins =", newins.listofvalues
# Gives the following output as I would expect based on the explanations.
# prints ins = [4, 3, 5]
# prints newins = [4, 3, 5]


newins.listofvalues = [10, 11]
print "ins = ", ins.listofvalues
print "newins = ", newins.listofvalues
# Gives
# ins = [4, 3, 5]
# newins = [10, 11]
# This is the output that I do not understand. 
# Why does ins.listofvalues not change this case.**

2 个答案:

答案 0 :(得分:7)

在Python中,object的字段保持对对象的引用。因此,当您在示例中指定新列表时,您将更改该字段引用的对象,而不是其内容。在做作之前,两个对象的listofvalues属性引用相同的列表,但在做作之后,它们引用了两个不同的列表。

这相当于以下代码:

>>> a = [4, 5]
>>> b = a
>>> b.append(3)
>>> b
[4, 5, 3]
>>> a
[4, 5, 3]
>>> b = [6, 7]
>>> b
[6, 7]
>>> a
[4, 5, 3]

如果要更改列表的内容而不是参考,则需要使用切片。那就是:

>>> a = [4, 5, 3]
>>> b = a
>>> b[:] = [6, 7]
>>> b
[6, 7]
>>> a
[6, 7]

注意:以下内容基于我对Python 2.6内部的理解。因此它实际上是特定于实现的,但它给你的心智模型可能非常接近语言规则的编写方式,并且应该适用于任何实现。

在Python中,始终通过引用访问对象(如Java,而不是C ++)。但是,变量名称或属性名称可以作为字典中的绑定,并在CPython中实现(除了局部变量优化,__slots__的存在,或通过{暴露的伪属性) {1}}和朋友们。)

在Python中,每个对象作为私有字典将其每个属性名称映射到该值。并且解释器有两个私有字典,它将本地变量名和全局变量名之间的映射保存到它们的值。当您更改对象的变量或属性的值时,您只需更改相应字典中的绑定。

因此,在您的示例中,您具有与以下代码中相同的行为:

__getattr__

答案 1 :(得分:3)

ins.listofvalues不会更改,因为您使用新列表替换了newins属性,而append()不会通过修改它来替换该对象。因此,现在两个不同的属性指向不同的列表。

无需复制即可获得相同的效果:

>>> ins = [1,2,3]
>>> newins = ins
>>> 
>>> ins.append(4)
>>> newins
[1, 2, 3, 4]
>>> 
>>> ins = [5,6,7]
>>> newins
[1, 2, 3, 4]

因此,您正确理解复制/深度复制,问题是Python中变量如何工作的常见误解。它们是引用,但它们不是指针。通常比“标签”更好地理解它。在这种情况下,您将原始列表标记为ins.listofvalues,然后将该标签复制到newins.listofvalues。但是当你重新分配ins.listofvalues时,你会把那个标签粘贴到一个新对象上。