Кодировка Python base64, а затем декодирование универсального объекта - PullRequest
0 голосов
/ 25 мая 2018

Я пытаюсь преобразовать numpy.ndarray в base64, а затем преобразовать его обратно.Можно ли использовать библиотеку base64?очень простой код ниже даже не работает, как ожидалось.Чего мне не хватает?

import numpy as np

x = np.array([[1, 2, 3], [4, 5, 6]], np.int32)
print(x)
print(type(x))

encoded = base64.b64encode(x)
decoded = base64.b64decode(encoded)
print(decoded)
print(type(decoded))

Есть ли способ получить исходную переменную?

Общий вопрос: могу ли я преобразовать «любой» объект в двоичную строку, а затем преобразоватьвернуться к исходному типу?

Может быть, я могу использовать pickle, НО мне нужен сжатый формат (не в файле): что-то вроде

x_compressed = zipped(pickle.dumps(x))

Ответы [ 2 ]

0 голосов
/ 25 мая 2018

Я не уверен, что вы пытаетесь достичь, но вы можете base-64 кодировать любой объект, который имеет представление bytes.В приведенном вами примере вы кодируете пустой массив в base64.

Это работает, потому что пустой массив имеет форму bytes.Вы можете достичь его, обернув bytes() вокруг массива или используя метод .tobytes().

import numpy as np

x = np.array([1,2,3])

bytes(x)
# returns:
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'

x.tobytes()
# returns:
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00'

Поскольку у нас есть bytes представление массива, вы можете передать его в base64кодировщик.Обратите внимание, что если объект не является байтовоподобным объектом, он base64 попытается преобразовать его перед кодированием, как в следующем примере:

base64.b64encode(x)
# returns
b'AQAAAAIAAAADAAAA'

base64.b64encode(x.tobytes())
# returns
b'AQAAAAIAAAADAAAA'

Массив байтов не является чем-то особенным.Это просто последовательность чисел!Вот и все.Вы полагаете, что не восстановили массив numpy, потому что процесс кодирования-декодирования все еще просто оставляет вас с результатом x.tobytes(), а не x.

Чтобы вернуть исходный объект, вам необходим интерфейс, который может считывать последовательность байтов и возвращать какой-либо объект.К счастью, numpy может сделать это с помощью функции frombuffer.Однако вам нужно будет указать numpy, какой тип массива он читает в байтах.

Другими словами, у вас может быть массив int32 и массив int16, которые имеют идентичные представления байтов, но для восстановления правильного вам нужно указать numpy, какой тип TYPE правильный.Так что вам нужны какие-то знания об объекте.

x = np.array([1,2,3])

# encode as base 64
x_64 = base64.b64encode(x.tobytes())

# decode back to bytes
x_bytes = base64.b64decode(x_64)

# use numpy to recreate original array of ints
np.frombuffer(x_bytes, dtype=int)
# returns:
np.array([1, 2, 3])

Если вы хотите сохранить объект, а затем восстановить его позже, этот процесс называется сериализацией.Есть два очень хороших пакета, которые обрабатывают сериализацию, первый находится в стандартной библиотеке, вызывает pickle, второй называется dill и может обрабатывать более сложные объекты.

import pickle

x = np.array([1,2,3])
pickled_x = pickle.dumps(x)
# pickled_x is a bytes-object that is a hard to read by humans. 

pickle.loads(x)
# returns:
np.array([1, 2, 3])
0 голосов
/ 25 мая 2018
Кодировка

base64, вероятно, не лучший выбор здесь.Это может быть хорошим выбором, если вам нужно хранить или передавать данные в средах, которые, возможно, по устаревшим причинам, ограничены ASCII.

Для массивов с пустым массивом существуют более простые методы tostring и frombuffer спредостережение, что вам нужно знать dtype и форму.Если вы используете фиксированные размеры и типы, вы можете использовать его напрямую:

>>> x
array([[1, 2, 3],
       [4, 5, 6]], dtype=int32)
>>> x.tostring()
b'\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00'
>>> np.frombuffer(x.tostring(), dtype=x.dtype).reshape(x.shape)
array([[1, 2, 3],
       [4, 5, 6]], dtype=int32)

В противном случае, просто используйте сериализатор и десериализатор numpy, np.save и np.load, которые позаботятся об этих деталях для вас.:

>>> import io
>>> f = io.BytesIO()
>>> np.save(f, x)
>>> f.getvalue()
b"\x93NUMPY\x01\x00v\x00{'descr': '<i4', 'fortran_order': False, 'shape': (2, 3), }                                                          \n\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00"
>>> f.seek(0); np.load(f)
array([[1, 2, 3],
       [4, 5, 6]], dtype=int32)

Примечание: Вы всегда можете добавить кодирование / декодирование b64 для этих сериализованных байтов (т.е. строки f.getvalue()), если вам нужно - но для всех практических целей вывозможно, можно просто транспортировать байты numpy напрямую, без преобразования в / из b64.

Общий вопрос: могу ли я преобразовать «любой» объект в двоичную строку и затем преобразовать обратно в исходный тип?

Теоретически, да, потому что объекты в любом случае являются просто байтами на вашем компьютере.Для общей сериализации объектов Python стандартная библиотека предоставляет pickle.Сжатие можно добавить с помощью zlib, чтобы привести только один пример.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...