首页 > 解决方案 > 在元组赋值(Python)中使用切片 Numpy 数组的机制是什么?

问题描述

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

a, b = b, a

b工作原理是先将and打包a成一个元组(b, a),然后将其解包到aand b,从而实现在一个语句中交换两个名称。但我发现 ifab被切片的 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]and的内存位置的“指针”元组a[0]。解包时, firsta[:2]被从内存开始的值覆盖a[2]。然后a[2:]被从 开始的值覆盖a[0]。这是对机制的正确理解吗?

标签: pythonarraysnumpyslicenumpy-slicing

解决方案


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

如果你这样做:

a[ix] = b

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

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

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

所以在这种情况下,b上面实际上是对nd.array.__getitem__. 它返回一个视图

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

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

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

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

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


推荐阅读