Python Cython:为什么NumPy数组需要类型转换为对象?

Python Cython:为什么NumPy数组需要类型转换为对象?,python,numpy,cython,Python,Numpy,Cython,我在《世界新闻报》上见过几次这样的事情: 但是,问题似乎并不是元组解包本身。这将以相同的错误失败 def test_castobj(ndarray[float64_t, ndim=2] arr): cdef: # Py_ssize_t b1, b2 ndarray[float64_t, ndim=2] zeros zeros = np.zeros(arr.shape, dtype=np.float64) return zeros 看起

我在《世界新闻报》上见过几次这样的事情:

但是,问题似乎并不是元组解包本身。这将以相同的错误失败

def test_castobj(ndarray[float64_t, ndim=2] arr):

    cdef:
        # Py_ssize_t b1, b2
        ndarray[float64_t, ndim=2] zeros

    zeros = np.zeros(arr.shape, dtype=np.float64)
    return zeros
看起来,这里没有发生元组解包。元组是
np.zero
的第一个参数

def test_castobj(ndarray[float64_t, ndim=2] arr):
    """This works"""
    cdef:
        Py_ssize_t b1, b2
        ndarray[float64_t, ndim=2] zeros

    b1, b2 = (<object> arr).shape
    zeros = np.zeros((<object> arr).shape, dtype=np.float64)
    return b1, b2, zeros
例如:

>>> from shape import test_castobj
>>> arr = np.arange(6, dtype=np.float64).reshape(2, 3)

>>> test_castobj(arr)
(2, 3, array([[0., 0., 0.],
        [0., 0., 0.]]))
*也许这与
arr
是一个记忆视图有关?但这是一种冒险


另一个例子是Cython:

在这种情况下,简单地索引
arr.shape[i]
就可以防止错误,我觉得这很奇怪

这也适用于:

def test_castobj(object[float64_t, ndim=2] arr):
    cdef ndarray[float64_t, ndim=2] zeros
    zeros = np.zeros(arr.shape, dtype=np.float64)
    return zeros

你是对的,这与Cython下的元组解包无关

原因是,
cnp.ndarray
不是一个普通的numpy数组(这意味着一个numpy数组,其接口从python中已知),而是numpy的C实现的一部分(在python中称为
np.array
):

shape
实际上映射到基础C-stuct的(
npy\u intp*shape“dimensions”
而不是简单的
npy\u intp*dimensions
)。这是一个技巧,所以你可以写

mat.shape[0]
它的外观(在某种程度上还有感觉)就像调用了numpy的python属性
shape
。但事实上,直接通往潜在C-stuct的捷径被采用了

顺便说一句,调用python-
shape
代价很高:必须创建一个元组并用
维度的值填充,然后访问第0个元素。另一方面,Cython的方法要便宜得多——只需访问正确的元素

但是,如果您还想访问数组的python属性,则必须将其强制转换为普通python对象(即,忘记这是
ndarray
),然后通过常用的python机制将
shape
解析为tuple属性调用

因此,基本上,即使这样做很方便,您也不希望像在pandas代码中那样在一个紧密的循环中访问numpy数组的维度,相反,您需要执行更详细的性能变量:

...
N=mat.shape[0]
K=mat.shape[1]
...


为什么可以在函数签名中编写
object[cnp.float64\u t]
或类似的函数,这让我感到很奇怪,因为参数显然被解释为一个简单的对象。也许这只是一个bug。

我不知道Cpython,但至少在
c
中,如果
arr.shape
返回一个指针(似乎是这样),您(和numpy)没有方法知道它的维度。
cpdef int sum3d(int[:, :, :] arr) nogil:
    cdef size_t i, j, k
    cdef int total = 0
    I = arr.shape[0]
    J = arr.shape[1]
    K = arr.shape[2]
def test_castobj(object[float64_t, ndim=2] arr):
    cdef ndarray[float64_t, ndim=2] zeros
    zeros = np.zeros(arr.shape, dtype=np.float64)
    return zeros
ctypedef class numpy.ndarray [object PyArrayObject]:
    cdef __cythonbufferdefaults__ = {"mode": "strided"}

    cdef:
        # Only taking a few of the most commonly used and stable fields.
        # One should use PyArray_* macros instead to access the C fields.
        char *data
        int ndim "nd"
        npy_intp *shape "dimensions"
        npy_intp *strides
        dtype descr
        PyObject* base
mat.shape[0]
...
N=mat.shape[0]
K=mat.shape[1]
...