Расширение Python - создание и проверка больших целых чисел эффективно

У меня есть родная библиотека, для которой естественный интерфейс будет включать передачу потенциально больших чисел. Я ожидаю, что половина будет < 32 бита; другой квартал < 64 бит; следующий восьмой < 128 бит - и т.д. Без ограничения фиксированной длины.

PyLong_FromUnsignedLongLong() и PyLong_AsUnsignedLongLong() были бы подходящими, если бы я мог ограничить значения, которые нужно поместить в один регистр.

PyLong_FromString() преодолевает это, но при нежелательном расходе требуется промежуточное представление. _PyLong_FromByteArray() и _PyLong_AsByteArray() смягчают эту стоимость (делая это промежуточное представление простым), но ведущее подчеркивание заставляет задуматься, может ли это привести к проблемам с переносимостью.

В longintrepr.h я нашел struct _longobject... который намекает, что это может быть способ напрямую взаимодействовать с внутренним представлением... хотя отсутствие подробной документации об этой структуре остается препятствием.

Какой подход приведет к оптимальной пропускной способности между Python и библиотекой? Есть ли документация, которую я пропустил?

Ответ 1

Префикс подчеркивания в значительной степени означает то же самое в C API, что и в обычном Python: "эта функция представляет собой деталь реализации, подлежащую изменению, поэтому следите за собой, если вы ее используете". Вам не запрещено использовать такие функции, и если это единственный способ достичь определенной цели (например, значительный выигрыш в эффективности в вашем случае), тогда вам будет удобно использовать API, если вы знаете об опасности.

Если API _PyLong_FromByteArray был действительно закрытым, это была бы функция static и не была бы полностью документирована и экспортирована в longobject.h. Фактически, Тим Петерс (известный разработчик Python) явно благословляет его использование:

[Дэн Кристенсен]

Мой ученик и я пишем расширение C, которое создает большой   integer в двоичном формате, который мы хотели бы преобразовать в python long.   количество бит может быть намного больше 32 или даже 64. Мой ученик нашел   функция _PyLong_FromByteArray в longobject.h, которая точно   что нам нужно, но ведущий знак подчеркивает меня настороженно. Безопасно ли   используйте эту функцию?

Python использует его внутри, поэтому лучше быть; -)

Будет ли он продолжать существовать в будущих версиях python?

Нет гарантий, и поэтому у него есть ведущее подчеркивание: это не официально поддерживаемая, задокументированная частью, часть рекламируемого API Python/C. Так получилось, что я добавил эту функцию, потому что Python нуждался в некоторой форме своей функциональности внутри разные C-модули. Сделать его официальной частью API Python/C было бы намного больше работы (которой у меня не было времени), и создало вечное новое бремя обслуживания (которое я не увлекаюсь независимо; -)).

На практике мало кто затрагивает эту часть реализации Python, поэтому Я не ожидаю/он уйдет или даже изменится на долгие годы. Самая большая незащищенность, о которой я могу думать, - это то, что кто-то может запустите крестовый поход, чтобы создать какой-то другой интерфейс байтового массива ↔ long "официальный", основанный на другом способе представления отрицательных целых чисел. Но даже тогда я ожидаю, что текущие неофициальные функции останутся, поскольку представление 256-дополнений остается необходимым для struct модуль "q", а для модуля протокола pickle= 2 длинный формат сериализации.

Или есть ли другой метод, который мы должны использовать?

Нет. Вот почему эти функции были изобретены для начала: -)

Здесь документация (из Python 3.2.1):

/* _PyLong_FromByteArray:  View the n unsigned bytes as a binary integer in
   base 256, and return a Python long with the same numeric value.
   If n is 0, the integer is 0.  Else:
   If little_endian is 1/true, bytes[n-1] is the MSB and bytes[0] the LSB;
   else (little_endian is 0/false) bytes[0] is the MSB and bytes[n-1] the
   LSB.
   If is_signed is 0/false, view the bytes as a non-negative integer.
   If is_signed is 1/true, view the bytes as a 2's-complement integer,
   non-negative if bit 0x80 of the MSB is clear, negative if set.
   Error returns:
   + Return NULL with the appropriate exception set if there not
     enough memory to create the Python long.
*/
PyAPI_FUNC(PyObject *) _PyLong_FromByteArray(
    const unsigned char* bytes, size_t n,
    int little_endian, int is_signed);

Основная причина, по которой это API-интерфейс с подчеркиванием-префиксом, заключается в том, что он зависит от реализации Python long как массива слов в базе данных "сила-два". Это вряд ли изменится, но поскольку вы используете API поверх этого, вы можете изолировать своих абонентов от изменений в API Python позже.

Ответ 2

Похоже, вам нужно PyNumber_Long. Некоторые хиты doc здесь.