Python: похожая функциональность в структуре и массиве по сравнению с типами - PullRequest
0 голосов
/ 24 августа 2018

Python предоставляет следующие три модуля, которые имеют дело с типами C и способами их обработки:

  • struct для структур C
  • array для массивов, таких как массивы в C
  • ctypes для функций C, что обязательно подразумевает работу с системой типов C

ctypes кажется более общим и гибким (его основная задача - «библиотека сторонних функций для Python»), чем struct и array, и между этими тремя модулями существует значительное совпадение в функциональности, когда задача состоит в чтении двоичного файла.структуры данных.Например, если бы я хотел прочитать структуру C

struct MyStruct {
    int a;
    float b;
    char c[12];
};

, я мог бы использовать struct следующим образом:

a, b, c = struct.unpack('if12s', b'\x11\0\0\0\x12\x34\x56\x78hello world\0')
print(a, b, c)
# 17 1.7378244361449504e+34 b'hello world\x00'

С другой стороны, , используя ctypesодинаково хорошо работает (хотя и немного более многословно):

 class MyStruct(ctypes.Structure):
     _fields_ = [
         ('a', ctypes.c_int),
         ('b', ctypes.c_float),
         ('c', ctypes.c_char * 12)
     ]
 s = MyStruct.from_buffer_copy(b'\x11\0\0\0\x12\x34\x56\x78hello world\0')
 print(s.a, s.b, s.c)
 # 17 1.7378244361449504e+34 b'hello world'

(в сторону: мне интересно, куда в этой версии пошёл трейлинг '\0', хотя ...)

Thisмне кажется, что он нарушает принципы «Дзен Питона»:

Должен быть один - и предпочтительно только один - очевидный способ сделать это.

Так как же возникла эта ситуация с несколькими из подобных модулей для обработки двоичных данных?Есть ли историческая или практическая причина?(Например, я мог бы представить полное исключение модуля struct и простое добавление более удобного API для чтения / записи структур C в ctypes.)

1 Ответ

0 голосов
/ 27 августа 2018

Отказ от ответственности: этот пост является спекуляцией, основанной на моем понимании «разделения труда» в Python stdlib, а не на фактической справочной информации.

Ваш вопрос проистекает из того факта, что «структуры C» и «двоичные данные», как правило, используются взаимозаменяемо, что, хотя и правильно на практике, неверно в техническом смысле. Документация struct также вводит в заблуждение: она утверждает, что работает над "структурами C", в то время как лучшим описанием были бы "двоичные данные" с некоторыми оговорками об совместимости с Си.

По сути, struct, array и ctypes делают разные вещи . struct имеет дело с преобразованием значений Python в двоичные форматы в памяти. array имеет дело с эффективным хранением большого количества значений. ctypes имеет дело с языком C (*). Перекрытие в функциональности связано с тем фактом, что для C «двоичные форматы в памяти» являются собственными, а «эффективное хранение значений» означает упаковку их в массив, подобный C.

Вы также заметите, что struct позволяет вам легко указывать порядковый номер, потому что он имеет дело с упаковкой и распаковкой двоичных данных многими различными способами, которыми они могут быть упакованы; в то время как в ctypes труднее получить не собственный порядок байтов, потому что он использует порядок байтов , который является родным для C .

Если ваша задача - чтение бинарных структур данных, уровни абстракции возрастают:

  1. Разделение байтового массива вручную и преобразование частей с помощью int.from_bytes и т. П.
  2. Описание данных в виде строки формата и использование struct для распаковки за один раз
  3. Использование библиотеки типа Construct для декларативного описания структуры в логических терминах.

ctypes здесь даже не фигурируют, потому что для этой задачи использование ctypes в значительной степени требует двустороннего обхода другого языка программирования . Тот факт, что это работает так же хорошо для вашего примера, является случайным; это работает, потому что C изначально подходит для выражения многих способов упаковки двоичных данных. Но если бы ваша структура была, например, со смешанным порядком байтов, это было бы очень трудно выразить в ctypes. Другим примером является float с половинной точностью, который не имеет эквивалента C (см. здесь ).

В этом смысле также очень разумно, чтобы ctypes использовал struct - в конце концов, "упаковка и распаковка двоичных данных" - это подзадача "взаимодействия с C".

С другой стороны, struct не имеет смысла использовать ctypes: это все равно, что использовать библиотеку email для преобразования кодировки символов, потому что это задача, которую может выполнять библиотека электронной почты.

(*) ну в принципе. Более точным было бы что-то вроде «сред на основе C», то есть того, как современные компьютеры работают на низком уровне благодаря коэволюции с C в качестве основного языка систем.

...