Python 数组切片行为

Python 数组切片行为,python,arrays,numpy,slice,Python,Arrays,Numpy,Slice,在切片时,为什么numpy.array的行为与Python的列表和默认数组不同?请考虑下面的例子: 1使用列表:语句b=a[1:3]创建一个新的列表对象,修改b不会修改a 2使用array.array:语句b=a[1:3]再次创建一个新的数组对象,修改b不会修改a 3使用numpy.array:语句b=a[1:3]似乎引用了原始列表的值,修改它也会修改a >>> import numpy >>> a = numpy.array([1,2,3,4]) >&

在切片时,为什么numpy.array的行为与Python的列表和默认数组不同?请考虑下面的例子:

1使用列表:语句b=a[1:3]创建一个新的列表对象,修改b不会修改a

2使用array.array:语句b=a[1:3]再次创建一个新的数组对象,修改b不会修改a

3使用numpy.array:语句b=a[1:3]似乎引用了原始列表的值,修改它也会修改a

>>> import numpy
>>> a = numpy.array([1,2,3,4])
>>> b = a[1:3]
>>> print(b)
[2 3]
>>> b[0] = -17
>>> print(b)
[-17   3]
>>> print(a)
[  1 -17   3   4]

问题是:为什么numpy中会出现这种行为?

因为numpy是一种高性能的数据采集。为了让Python创建一个新列表,它必须构造一个新列表,增加列表中每个元素的所有指针,将该项添加到列表中,然后返回切片。NumPy很可能只是增加开始数组的偏移量,并更改数组的结尾

NumPy切片

将NumPy数组想象成这样是的,这是非常简单的:

struct array
{
    size_t type_size;
    size_t length
    void* start;
};
如果您不知道C,那么基本上意味着数组可以被视为内存地址,指定数组的开始,它存储所存储的每种类型的大小,然后是缓冲区的长度。对于整数数组,类型_的大小可能为4,在本例中,对于20字节的缓冲区,长度可能为5

在切片时,NumPy可以简单地增加起始值并减小大小,而不是复制整个数据

array slice(array* array, size_t start, size_t end)
{
    array arr = *array;
    arr.start = (char*)arr.start + start;
    arr.length = end - start;
    return arr;
}
这比为一个新列表分配内存,然后赋值和递增要便宜得多,Python将引用计数这些指针到列表中

Python切片

下面是一个简化的Python示例:

PyObject* slice(PyObject* list, size_t start, size_t end)
{
    size_t length = end - start;
    PyObject* out = PyList_New(length);
    for (size_t i = start; size_t i < end; ++i) {
        PyObject*item = PyList_GetItem(list, i);
        PyList_Append(&out, i);
    }

    return out;
}
注意到这有多复杂?还有更多的东西藏在引擎盖下

理性的

思考性能:为了使NumPy具有原始的切片行为,它必须在内存中占用一个新地址,因为数据在内存中是连续的。这意味着可能通过memcpy复制数据。这很昂贵:假设我有一个20000 np.int32~80 KB的数组,我需要将所有这些数据复制到一个新数组中。在上面的切片示例中,假设8字节大小和指针,我只复制了约24字节的内存

array slice(array* array, size_t start, size_t end)
{
    array arr = *array;
    arr.start = (char*)arr.start + start;
    arr.length = end - start;
    return arr;
}
PyObject* slice(PyObject* list, size_t start, size_t end)
{
    size_t length = end - start;
    PyObject* out = PyList_New(length);
    for (size_t i = start; size_t i < end; ++i) {
        PyObject*item = PyList_GetItem(list, i);
        PyList_Append(&out, i);
    }

    return out;
}