В последних версиях Python в операцию сериализации было введено понятие протоко лов — форматов хранения сериализованных данных. Чтобы определить желаемый протокол, необходимо передать дополнительный параметр функциям сериализации (его не требуется передавать функциям обратного преобразования: протокол определяется автоматически по сериализованным данным):
pickle.dump(object, file, protocol) # или именованный аргумент protocol=N
Сериализация данных может быть выполнена с применением текстового или двоичного протокола — двоичный протокол позволяет получить более эффективный формат, но при этом создаются нечитаемые человеком файлы. По умолчанию в Python 3.X используется исключительно двоичный формат (известный также, как протокол 3). В текстовом режиме (протокол 0) сериализованные данные представляют собой печатаемый текст ASCII, который может читаться человеком (по сути, он представляет собой последовательность инструкций для машины стека), но в Python 3.X в любом случае получается объект bytes. Другие протоколы (протоколы 1 и 2) также создают сериализованные данные в двоичном формате.
В Python 3.X независимо от номера протокола сериализованные данные представляют собой объект bytes, а не str, и именно поэтому при сохранении и чтении их в плоских файлах требуется использовать двоичный режим (причины описываются в главе 4, если вы забыли). Аналогично, имитируя интерфейс объектов файлов, мы должны использовать объекты bytes:
> >> import io, pickle
> >> pickle.dumps([1, 2, 3]) # по умолчанию=двоич. протокол
b’\x80\x03]q\x00(K\x01K\x02K\x03e.’
> >> pickle.dumps([1, 2, 3], protocol=0) # протокол формата ASCII b'(lp0\nL1L\naL2L\naL3L\na.’
> >> pickle.dump([1, 2, 3], open(‘temp’,’wb’)) # даже если protocol=0, ASCII
> >> pickle.dump([1, 2, 3], open(‘temp’,’w’)) # при чтении необх. режим ‘rb’ TypeError: must be str, not bytes
> >> pickle.dump([1, 2, 3], open(‘temp’,’w’), protocol=0)
TypeError: must be str, not bytes
> >> B = io.BytesIO() # использовать двоичные потоки/буферы
> >> pickle.dump([1, 2, 3], B)
> >> B.getvalue()
b’\x80\x03]q\x00(K\x01K\x02K\x03e.’
> >> B = io.BytesIO() # bytes и для формата ASCII
> >> pickle.dump([1, 2, 3], B, protocol=0)
> >> B.getvalue()
b'(lp0\nL1L\naL2L\naL3L\na.’
> >> S = io.StringIO() # это не объект str
> >> pickle.dump([1, 2, 3], S) # даже если protocol=0, ASCII
TypeError: string argument expected, got ‘bytes’
> >> pickle.dump([1, 2, 3], S, protocol=0)
TypeError: string argument expected, got ‘bytes’
Имеется еще один родственный модуль, _pickle, написанная на языке C оптимизация модуля pickle, который автоматически используется модулем pickle, если доступен — его не требуется выбирать вручную или использовать непосредственно. Модуль shelve наследует эту оптимизацию автоматически. Я еще не рассказывал о модуле shelve, но сейчас сделаю это.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011