Python混淆 - 约定,名称和值

时间:2017-02-21 20:09:34

标签: python

我是初学者,在学习python时会感到困惑。如果我有以下python代码:

import numpy as np
X = np.array([1,0,0])
Y = X
X[0] = 2
print Y

Y将显示为array([2, 0, 0])

但是,如果我执行以下操作:

import numpy as np
X = np.array([1,0,0])
Y = X
X = 2*X
print Y

Y仍为array([1,0,0])

发生了什么事?

4 个答案:

答案 0 :(得分:8)

这样想: python中的等号分配引用。

Y = X使Y指向X指向

的相同地址

X[0] = 2使x [0]指向2

X = 2*X使X指向一个新东西,但Y仍指向原始X的地址,因此Y未更改

这不完全正确,但它足够接近理解原则

答案 1 :(得分:5)

因为XY 引用指向同一个对象np.array([1,0,0]),这意味着无论是否完成了通话通过 XY,结果将是相同的,但更改一个的引用,无效。

如果你写:

X = np.array([1,0,0])
Y = X

基本上发生的事情是,有两个局部变量 XY 引用到同一个对象。所以内存看起来像:

     +--------+
Y -> |np.array| <- X
     +--------+
     |[1,0,0] |
     +--------+

现在,如果你做的X[0] = 2基本上是短的:

X.__setitem__(0,2)

所以你在对象上调用一个方法。所以现在内存看起来像:

     +--------+
Y -> |np.array| <- X
     +--------+
     |[2,0,0] |
     +--------+

如果你写了:

X = 2*X

第一个2*X 已评估。现在2*X是简称:

X.__rmul__(2)

(Python首先查看2是否支持__mul__的{​​{1}},但由于X将引发2),Python将回退到{{1} }})。现在NotImplementedException 不会更改X.__rmul__:它会保留X.__rmul__完整,但会构建一个新数组并返回该数组。 X捕获现在引用该数组的新数组。

创建一个新的X对象:X,然后array 引用该新对象。所以现在内存看起来像:

array([4, 0, 0])

但正如您所见,X仍然引用旧对象

答案 2 :(得分:3)

这更多是关于约定和名称而不是引用和值。

分配时:

Y = X

然后名称Y引用名称X指向的对象。在某种程度上,指针XY指向同一个对象:

X is Y   # True

is检查名称是否指向同一个对象!

然后它变得棘手:你在阵列上做了一些操作。

X[0] = 2

这称为“项目分配”并调用

X.__setitem__(0, 2)

__setitem__应该做什么(约定)是更新容器X中的某个值。因此,X之后仍应指向同一个对象。

然而X * 2是“乘法”,并且约定声明这应该创建一个新对象(再次约定,您可以通过覆盖X.__mul__来改变该行为)。所以当你这样做时

X = X * 2

名称X现在引用X * 2创建的新对象:

X is Y   # False

通常常见的库遵循这些约定,但重要的是要强调你可以完全改变它!

答案 3 :(得分:2)

当您说 +--------+ +--------+ Y -> |np.array| X ->|np.array| +--------+ +--------+ |[2,0,0] | |[4,0,0] | +--------+ +--------+ 时,您创建的对象包含一些方法和一些内部缓冲区,其中包含实际数据和其他信息。

执行Y设置X = np.array([1, 0, 0])以引用相同的实际对象。这称为绑定到Python中的名称。您已将绑定到Y = X的同一对象绑定到名称Y

执行X调用对象的Y方法,该方法对底层缓冲区执行一些操作。如果修改了对象。现在,当您打印X[0] = 2__setitem__的值时,来自该对象缓冲区的数字为X

执行Y会转换为2, 0, 0。此方法不会修改X = 2 * X。它创建并返回一个新的数组对象,每个数组对象的元素是X.__rmul__(2)的相应元素的两倍。然后将新对象绑定到名称X。但是,名称X仍然绑定到原始数​​组,因为您没有做任何更改。另外,使用X是因为Y不起作用。 Numpy数组自然地将乘法定义为可交换,因此X.__rmul__2.__mul__(X)应该是相同的。

值得注意的是,您还可以执行X.__mul__,这会将更改传播到X.__rmul__。这是因为X *= 2运算符转换为Y方法, 修改输入。