#python #list #io #byte
#python #Список #io #байт
Вопрос:
У меня есть список в Python, который состоит из кортежей, имеющих следующий формат: (int, float). Я хочу записать этот список в байт ввода-вывода или необработанный поток ввода-вывода без необходимости преобразовывать целые числа и / или числа с плавающей точкой в строку. Как я могу это сделать? Спасибо.
Комментарии:
1. Если вы хотите записать эти объекты в поток, вы должны преобразовать их в байты, так или иначе, т.е. сериализовать их. Если вы не хотите использовать string в качестве формата, вам нужно выбрать какой-либо другой формат. Чего вы хотите? Почему вам не нужны строки?
2. Я не хочу преобразовывать их в строки, потому что я передаю данные с сервера клиенту и хочу минимизировать размер полезной нагрузки. Мои целые числа длинные и занимают 32 байта. Мои значения с плавающей точкой составляют 24 байта. Если я преобразую их в строки, размер полезной нагрузки увеличится, потому что каждый символ в строке занимает 1 байт. Но как мне сериализовать и разсериализировать их в / из байтов? Спасибо за вашу помощь.
Ответ №1:
Существует множество форматов, которые можно использовать для сериализации объектов Python в байты. Для каждого из них есть свои плюсы и минусы.
Если данные содержат только список кортежей из целых чисел и flaots, это делает работу довольно простой.
Давайте предположим, что это данные:
data = 100 * [(1, 1.111), (18, 1.234), (555555, 0.001), (-1, 1e70)]
Какой из них попадает в категорию «строк», мне не ясно. Наиболее очевидным «строковым» форматом был бы str(data)
. Насколько он велик?
>>> len(str(data))
5500
Это занимает 5500 байт. Вопрос требует чего-то более сжатого. Итак, мы ищем что-то намного короче 5500 байт.
JSON — очень популярный формат (он также является строкой). Насколько он велик?
>>> len(json.dumps(data))
5500
Этот файл имеет тот же размер (5500 байт), но, по крайней мере, он четко определен. Может ли это быть меньше? Как насчет сжатого JSON?
>>> len(bz2.compress(json.dumps(data).encode('utf-8')))
131
Это намного лучше!
Вероятно, это было очень хорошо из-за повторяющегося шаблона. Существует ли формат, который не использует архивирование? Может быть, рассолить?
>>> len(pickle.dumps(data))
862
Не так хорошо, как zip (конечно!), но все равно хорошо.
Можем ли мы сделать BZipped pickle?
>>> len(bz2.compress(pickle.dumps(data)))
155
Лучше, но нет причин для того, чтобы это было лучше, чем сжатый JSON.
Как насчет какого-нибудь другого формата? Вы могли бы преобразовать каждый кортеж в эквивалент этой структуры языка Си, используя модуль struct:
struct {
int i;
double f;
};
Однако тогда вам нужно было бы знать, насколько большим может быть значение int. Python int может быть сколь угодно большим, но если вы, например, знаете, что все числа находятся в диапазоне от 0 до 255, вам нужен всего один байт. Для значения с плавающей точкой вам нужно 64 бита (т. Е. 8 байт), иначе вы потеряете точность. Таким образом, это займет около 1000 байт. Не очень хорошо.
Существуют также другие встроенные опции, описанные в документации Python по постоянству.
Вы также можете изобрести свой собственный формат.
В конце концов, вы должны решить, что подходит вам лучше всего.
Ответ №2:
Вы можете очень легко преобразовывать целые числа и числа с плавающей запятой в байты напрямую, используя модуль struct.
>>> import struct
>>> data = [(2, 1.0), (3, 2.0), (25, 55.5)]
>>> for tup in data:
bytes_data = struct.pack("<ld", *tup)
print(bytes_data)
b'x02x00x00x00x00x00x00x00x00x00xf0?'
b'x03x00x00x00x00x00x00x00x00x00x00@'
b'x19x00x00x00x00x00x00x00x00xc0K@'
Кроме того, строка, которую я использую в качестве первого аргумента pack
функции, является идентификатором формата, который сообщает вам, какой тип и размер каждого числа, в данном случае l
это значение с длинным знаком int, d
это значение с плавающей запятой double.