python何时知道初始化是一个副本,所以在一个地方的更改会自动更改另一个地方?

时间:2014-01-30 22:21:31

标签: python memory copy

如果我这样做:

x = 1
y = [x,x,x,x]
y[1] = 2
print y

我明白了:

[1,2,1,1]

但如果我这样做:

x = [1,1]
y = [x,x,x,x]
y[1][0] = 2
print y

我明白了:

[[2,1],[2,1],[2,1],[2,1]]

有人可以向我解释两者之间的细微差别吗?我的意思是,就像Python如何分配内存一样,在第一种情况下,y的四个元素读取不同的内存位置,但在第二种情况下,y的四个元素读取相同的位置?

为什么Python的行为如此?当我使用Matlab时,不会发生这样的事情。

谢谢。

3 个答案:

答案 0 :(得分:6)

Python中的所有变量都包含引用(指针)。即使是直接存储在C等其他语言中的变量中的简单类型(如整数)也会使用Python中的引用进行存储。为名称指定值会更改名称所指的内容(指向)。准确理解何时发生这种情况对理解Python的行为方式至关重要。

让我们开始吧:

a = 2           # points a to the integer object 2
a = 3           # points a to a different integer object, 3
b = [1, 2, 3]   # points b to a new list object [1, 2, 3]

下一步:

c = a            # a and c now point to the same integer object, 3
d = b            # b and d now point to the same list object, [1, 2, 3]

到目前为止这么好,对吗?现在你可以看到为什么它的工作方式如下:

d.append(4)      # b and d still point to the same list object, which is
                 # now [1, 2, 3, 4]
print(b)         # prints [1, 2, 3, 4] -- how could it not?

事实上,无论对象的类型如何,一切都以相同的方式工作。只是某些类型你不能“就地”改变它们中的数字,字符串和元组:

a += 2           # a now points to the integer object 5, because you can't
                 # change 2 into 5 (integers are immutable)
print(c)         # prints 3. c still points to 3, because you never told
                 # Python to make c point anywhere else!

可是:

b.append(5)      # doesn't change what b points to, just changes the list
b += [6]         # also (somewhat counterintuitively) doesn't change what b
                 # points to, even though it did with integers
print(d)         # prints [1, 2, 3, 4, 5, 6] because b and d still point to
                 # the same list

+=的情况有点令人困惑,因为它与列表和整数的行为完全不同。但是,请记住,+=(以及大多数其他Python操作)可以由对象本身重新定义。在这种情况下,+=由附加到整数和列表类型的方法__iadd__()处理。在整数上,+=返回一个新对象,因为它必须是整数,它是不可变的。在列表中,+=更有效地返回传递的相同对象,而不是复制。

总结一下:

  • Python变量(名称)包含对象的引用(指针)
  • 分配会更改变量(名称)引用的对象(指向)
  • 看起来像赋值的一些东西(特别是像+=这样的增强赋值)实际上不是赋值,而是方法调用,并不一定会改变变量(名称)引用的对象(指向) ,虽然他们可以

答案 1 :(得分:0)

在第一种情况下,x只是integer,而在第二种情况下,列出的是reference。 第一种情况下的分配会将x的内容复制到integer列表中。在分配之后,对x所做的任何更改都不会反映在y中,因为xy的元素存储在不同的内存位置。

但在第二种情况下,赋值y=[x,x,x,x]存储了4个相同列表[1,1]的引用。由于每个引用都指向相同的内存位置,因此更改其中任何一个或甚至更改原始x都会在任何地方反映相同的更改。

答案 2 :(得分:0)

问题在于,在第一个示例中,您正在更改y的内容,而在第二个示例中,您正在更改y内的列表内容

试试这个:

x = 1
y = [x,x,x,x]
print map(id,y)

x = [1,1]
y = [x,x,x,x]
print map(id,y)

输出(会有所不同):

[41548904, 41548904, 41548904, 41548904]
[50255344, 50255344, 50255344, 50255344]

请注意,两个列表都有相同对象的4倍,现在尝试:

x = 1
y = [x,x,x,x]
y[1] = 2
print map(id,y)

x = [1,1]
y = [x,x,x,x]
y[1] = [2,1]
print map(id,y)

输出(会有所不同):

[28224616, 28224592, 28224616, 28224616]
[36931056, 36929264, 36931056, 36931056]

请注意,您已在两个示例中更改了列表y的元素。

最后,如果你这样做:

x = [1,1]
y = [x,x,x,x]
y[1][0] = 2

您正在更改位置1上x元素的内容(与所有其他位置一样),因此当您打印y时,您将打印x四次第一个值改变了。