Функция в Cython меняет тип массива - PullRequest
1 голос
/ 02 июля 2019

Я работаю с Cython и numpy, и у меня есть странная проблема, связанная с функцией cython, изменяющей dtype элементов массива numpy.Как ни странно, dtype изменяется только тогда, когда тип ввода массива действительно указан.

Я использую Cython == 0.29.11, numpy == 1.15.4, python 3.6, в Ubuntu 18.04.

# cyth.pyx
cimport numpy as np

def test(x):
    print(type(x[0]))

def test_np(np.ndarray[np.uint32_t, ndim=1] x):
    print(type(x[0]))

Теперь цитонизируем этот файл и используем функции:

>>> from cyth import test, test_np
>>> import numpy as np
>>> a = np.array([1, 2], dtype=np.uint32)
>>> test(a)
<class 'numpy.uint32'>
>>> test_np(a)
<class 'int'>

Так что test работает как положено, печатая тип первого элемента во входном массиве - uint32.Но test_np, который фактически гарантирует, что типом входящего массива является uint32, теперь показывает обычный Python int как тип первого элемента.

Даже пытаясь заставить элемент быть правымtype не работает, т.е. использование:

def test_np(np.ndarray[np.uint32_t, ndim=1] x):
    cdef np.uint32_t el
    el = x[0]
    print(type(el))

все еще приводит к

>>> test_np(a)
<class 'int'>

Любая помощь в понимании этого несоответствия будет принята с благодарностью.

1 Ответ

2 голосов
/ 02 июля 2019

Cython не меняет тип массива, но возвращает элемент немного другого типа.

Данные в массиве numpy хранятся как непрерывное поле 32-битных целых чисел без знака. Доступ к x[0] означает создание объекта Python (поскольку интерпретатор Python не может обрабатывать необработанные C-ints) - numpy имеет выделенный класс-обертку для каждого типа numpy-d и возвращает np.uint32 -объект.

С другой стороны, Cython отображает все целочисленные типы C (например, long, int и т. Д.) Просто на целое число Python (что имеет смысл).

Теперь, когда numpy импортируется, x[0] больше не означает использование __getitem__() массива numpy (который будет возвращать np.uint32 -объект), а целое число C (в данном случае 4-байтовое число без знака), которое преобразуется в Python-целое число, потому что «return XXX» означает в функции def, что результатом должен быть объект Python.

Что означает, что массив имеет другой тип - типы отображаются по-разному при преобразовании в Python-объект с помощью Cython.


Если вы хотите получить доступ к данным как np.uint32 -объектам, вы можете вызвать __getitem__ вместо [..] ([..] переводится Cython как доступ к необработанным C-данным):

%%cython
cimport numpy as np

def test_np(np.ndarray[np.uint32_t, ndim=1] x):
    print(type(x[0]))                     # int
    print(type(x.__getitem__(0)))         # numpy.uint32

Когда вы используете типизированные представления памяти, а не ndarray, то прямой вызов __getitem__ вернет целое число Python __getitem__ представления памяти не вызывает __getitem__ базового ndarray, но обращается к данным на C -уровень. Чтобы вызвать __getitem__ базового объекта для просмотра в памяти:

def test_np(np.uint32_t[:] x):
    print(type(x[0]))
    print(type(x.base.__getitem__(0))) # instead of x.__getitem__(0)
...