Python - 数组复制/分配,numpy 的意外“=array[:]”行为

Python - array copying/assign, unexpected '=array[:]' behaviour for numpy

提问人:Wuhuuu2 提问时间:4/28/2020 最后编辑:Wuhuuu2 更新时间:4/29/2020 访问量:89

问:

我正在阅读有关通过引用或值复制数组 (&list) 的信息。但是,我在这里遇到了一个问题。为了说明我的问题,我举了三个例子,每个例子都有一个作业和一个更改。

第一个示例:默认情况下,它通过引用进行复制。
因此,该更改会产生 a 和 ArrayA,两者都具有相同的地址。还行

第二个示例:由于首先计算右侧,因此 *1 不会更改其值,但会导致按值复制。(我认为这也可以通过其他几种方式完成,例如使用 copy() 和 ..)
因此,更改仅影响 c,其地址与 ArrayC 不同。还行

第三个示例:据我了解,这里我将 [:] 添加到数组中,从而复制数组(=按值)。可以通过 e 和 ArrayE 的不同地址来确认。但是,此更改不仅影响 e,还影响 ArrayE。对我来说,这几乎是出乎意料的,因为它甚至向我展示了不同的地址。为什么?

提前致谢=)

import numpy as np
# Example 1, by reference
ArrayA = np.array([5,2,3,5,4])
ArrayB = np.array(  [1,2,3,4])

a = ArrayA
a[1:] += ArrayB

print("{}:\t{},\tid: {}".format("ArrayA",ArrayA, id(ArrayA) ))
print("{}:\t  {},\tid: {}".format("ArrayB",ArrayB, id(ArrayB) ))
print("{}:\t{},\tid: {}".format("a",a, id(a) ))

ArrayC = np.array([5,2,3,5,4])
ArrayD = np.array(  [1,2,3,4])


# Example 2, by value
c = ArrayC*1
c[1:] += ArrayD

print()
print("{}:\t{},\tid: {}".format("ArrayC",ArrayC, id(ArrayC) ))
print("{}:\t  {},\tid: {}".format("ArrayD",ArrayD, id(ArrayD) ))
print("{}:\t{},\tid: {}".format("c",c, id(c) ))

# Example 3, by reference/value?!?!
ArrayE = np.array([5,2,3,5,4])
ArrayF = np.array(  [1,2,3,4])

e = ArrayE[:]
e[1:] += ArrayF

print()
print("{}:\t{},\tid: {}".format("ArrayE",ArrayE, id(ArrayE) ))
print("{}:\t  {},\tid: {}".format("ArrayF",ArrayF, id(ArrayF) ))
print("{}:\t{},\tid: {}".format("e",e, id(e) ))
ArrayA: [5 3 5 8 8],    id: 2450575020480
ArrayB:   [1 2 3 4],    id: 2450575021680
a:      [5 3 5 8 8],    id: 2450575020480

ArrayC: [5 2 3 5 4],    id: 2450575021280
ArrayD:   [1 2 3 4],    id: 2450575022080
c:      [5 3 5 8 8],    id: 2450575022240

ArrayE: [5 3 5 8 8],    id: 2450575022640
ArrayF:   [1 2 3 4],    id: 2450575022000
e:      [5 3 5 8 8],    id: 2450575022880
Python 数组 列表 引用传递

评论

1赞 chepner 4/28/2020
“通过引用复制”不是一回事。你应该阅读 nedbatchelder.com/text/names.html,然后重新构建你的问题。
0赞 juanpa.arrivillaga 4/29/2020
它不是“引用复制”,它根本不是复制品。请注意,这与按引用传递和按值传递无关,后者是评估策略,即如何/何时计算函数参数的语义。请注意,Python 既不使用按值调用,也不使用按引用调用
0赞 juanpa.arrivillaga 4/29/2020
对对象进行切片时,它会创建一个新的数组对象,该对象是切片数中基础缓冲区的视图numpy.ndarray

答:

0赞 ywbaek 4/28/2020 #1

已编辑 - 请参阅下面的 @juanpa.arrivillaga 的评论。

在所有示例中,的
可变的对象。
因此,从第三个示例中,两者都指向相同的对象。
这就是为什么这些变化都反映在两者上的原因。
您可以通过检查他们的 ID 来验证这一点。
ndarraysnumpy.int32eArrayEnumpy.int32

print(id(e[0]) == id(ArrayE[0]))

评论

0赞 Wuhuuu2 4/29/2020
所以我是对的:所以使用 [:] 会产生一个不同的数组,就像我这样做的方式一样。但是新数组的字段保持不变,如您的匹配所示?id(e)id(e[0]))
0赞 ywbaek 4/29/2020
@Wuhuu2正确。如果使用常规的 python 列表而不是 numpy 数组,则值将是 int 类型,这是不可变的。因此,一个列表上的更改不会修改另一个列表。
0赞 juanpa.arrivillaga 4/29/2020
否,数组中的值及其可变性无关紧要。在 python 中对列表进行切片会使用浅拷贝语义创建一个新的列表对象。a 的切片创建一个新的数组对象,该对象是基础基元缓冲区的视图numpy.ndarray
0赞 juanpa.arrivillaga 4/29/2020
另请注意,数组实际上不包含对象。 具有数字/结构化 dtypes 的对象本质上是原始类 C 数组上的面向对象的包装器。 而各种 dtypes 实际上只在你访问数组中的值时才短暂存在,以便将其引入解释器级别,有点像 Java 中的自动装箱。numpy.int32numpy.ndarraynumpy.int32
0赞 ywbaek 4/29/2020
@juanpa.arrivillaga 这更有意义。谢谢!