在元组赋值 (Python) 中使用切片 Numpy 数组的机制是什么?

What is mechanism of using sliced Numpy arrays in a tuple assignment (Python)?

我知道Python中的元组赋值如下

a, b = b, a

的工作原理是先将ba打包成一个元组(b, a),然后解包为ab,从而实现交换一个声明中的两个名字。但是我发现如果 ab 被切片的 Numpy 数组替换:

# intended to swap the two halves of a Numpy array
>>> import numpy as np
>>> a = np.random.randn(4)
>>> a
array([-0.58566624,  1.42857044,  0.53284964, -0.67801528])
>>> a[:2], a[2:] = a[2:], a[:2]
>>> a
array([ 0.53284964, -0.67801528,  0.53284964, -0.67801528])

我的猜测是压缩元组 (a[2:], a[:2]) 实际上是指向 a[2]a[0] 内存位置的“指针”元组。解包时,首先 a[:2] 被从内存 a[2] 开始的值覆盖。然后 a[2:] 被从 a[0] 开始的值覆盖。这是对机制的正确理解吗?

所以,这不是简单的作业。名称-对象绑定语义适用于简单赋值,即 a = b.

如果你这样做:

a[ix] = b

然后确切的行为被推迟到类型,即它等同于

type(a).__setitem__(a, ix, b)

Numpy 数组基本上是原始的类 C 数组的面向对象包装器,实现了“真正的”多维数组接口。在这种情况下,要理解的关键是不同的 numpy 数组对象可以共享底层缓冲区。简单切片总是创建一个 numpy.ndarray 对象,它是原始数组的 view

所以在这种情况下,上面的b实际上是对nd.array.__getitem__的调用。其中returns一个视图.

因此,考虑 Python 列表的简单情况。右边:

(a[2:], a[:2]) 

创建一个包含两个的元组,独立 列表对象(尽管是浅复制)。

当它们被分配到左侧的分配目标序列时,突变没有任何共享效果。三个列表对象有三个独立的缓冲区(列表对象不会创建视图)。

另一方面,表达式 a[2:], a[:2] 创建一个元组,其结果是切片原始 nd.array 对象的结果,由 nd.array.__getitem__ 控制。这将创建两个新的数组对象,但它们在原始数组的基础缓冲区上 views。基本上,您在三个不同的数组对象之间共享相同的底层可变原始缓冲区。