Может ли массив символов без знака в Cython содержать ноль? - PullRequest
0 голосов
/ 28 сентября 2019

Я хочу ускорить некоторые основные подпрограммы в проекте Python с помощью Cython (я очень плохо знаком с обоими).Я пишу дополняющие файлы .pxd с информацией о типе для соответствующих .py.В одном .py у меня есть класс с переменной экземпляра array.array, который я хочу быть массивом беззнаковых символов в Cython.Он компилируется, но я обнаружил (после кропотливой отладки), что всякий раз, когда 0 записывается где-нибудь в массиве, его длина изменяется, и IndexError вызывается, если к массиву обращаются после элемента 0.

Здесьмаленький (не уверен, что он минимальный) пример.

ram.py:

import array

class Ram:

def __init__(self):
    self.ram = array.array('B', [1,1,0,1,1])
    print(len(self.ram))

ram.pxd:

cdef class Ram:
    cdef unsigned char[5] ram

Что я получаю после компиляции в расширениемодуль:

>>> import ram
>>> ram.Ram()
2

Я попытался отключить привязанные проверки с помощью директивы компилятора boundscheck = False, но безрезультатно.

Он работает как положено (дает длину 5), если я использую cdef unsigned int[5] ram в ram.pxd, но я хочу использовать массив байтов.

Как мне сохранить фиксированную длину моего массива, при этом сохраняя возможность записи в него нулей?

(Я использую Cython 0.29.13 и Python 3.7.4)

1 Ответ

0 голосов
/ 28 сентября 2019

Ваша проблема с len, а не с массивом.len - это функция Python, поэтому она не определена для массива char.Однако Cython пытается дать ответ и по умолчанию использует strlen подход к подсчету до первого 0 байтов.В этом случае это неправильно, но это разумное общее предположение.

Вы можете определить такой массив и хранить любые данные, включая 0.Вы просто не можете полагаться на len в Cython для получения длины - в этом случае длина является константой, так что вы знаете это, но если бы это был динамически распределенный массив , вы бы были бы ответственны за сохранениеЭто.Возможно, вам также придется позаботиться об автоконвертации Cython в строку Python.


Редактировать: Немного подробнее, так как я не думаю, что вы делаете достаточночто вы думаете:

cdef const char[5] ram

определяет массив C длиной 5. Это очень компактно (он не хранит никаких дополнительных данных, кроме 5 символов), быстрый доступ в Cython, но не имеетЭквивалент Python и, следовательно, для доступа к нему в Python требуется преобразование (либо автоматическое, либо что-то, что вы делаете самостоятельно)

ram = array.array(...)

копирует массив Python в массив C.

Я подозреваю, что вам следует использоватьвместо этого просмотр памяти:

cdef unsigned char[::1] ram # ::1 specifies C contiguous

Они немного менее экономичны (они хранят некоторую информацию о подсчете ссылок Python и форму), и в Cython их можно быстро получить (вы можете сделать их быстрее,отключение проверки границ и отрицательной индексации, а также указание смежного C, как я сделал здесь).Для вас важнее то, что к ним можно получить доступ в Python (хотя и с обычной скоростью Python), и Cython не будет пытаться рассматривать их как строки C.

ram = array.array(...)

создает представление данных, содержащихся в массиве(без копии - он делится данными).

...