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

14 浏览
0 Comments

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

如果我执行:

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时,不会出现这样的情况。

谢谢。

0
0 Comments

Python之所以选择这样的行为是为了简化语言的设计,因为所有变量都是指针,这样可以统一处理不同类型的对象。而其他语言之所以会有不一致的行为,是出于性能的考虑。C语言之所以将数组传递给函数时按引用传递,而将整数按值传递,是为了提高性能。而在Python中,一切都是以引用的方式处理,这样可以简化语言的实现,而性能虽然好,但并不是主要目标。

0
0 Comments

在第一种情况下,x只是一个整数,而在第二种情况下,它是一个指向列表的引用。

在第一种情况下的赋值操作将x的内容复制到一个整数的列表中。在赋值之后,对x的任何更改都不会反映在y中,因为x和y的元素存储在不同的内存位置。

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

哦,你是说这种类似复制的行为只发生在列表中吗?

整个列表并没有被复制到y中,只是一个引用被存储在y中。

0
0 Comments

问题的原因是在第一个例子中,您正在更改y的内容,而在第二个例子中,您正在更改y内的列表的内容。

解决方法是使用深拷贝而不是浅拷贝来创建y的副本。这样,当更改一个位置时,另一个位置也会自动更改。您可以使用copy模块中的deepcopy函数来实现深拷贝。

以下是修改后的代码示例:

import copy
x = 1
y = [x, x, x, x]
print(map(id, y))
x = [1, 1]
y = copy.deepcopy([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 = copy.deepcopy([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

假设x是一个列表。如果我执行id(x),我会得到指针id,但如果我执行map(id,x),我会得到x指向的内容的id。我对吗?

id返回Python对象的内部id,每个活动对象的id都是唯一的。请阅读这个

0