Диктофон в Python - PullRequest
       21

Диктофон в Python

0 голосов
/ 23 октября 2018

Могу ли я ожидать, что строковое представление одного и того же засеченного dict будет согласованным на разных машинах / прогонах для одной и той же версии Python?В рамках одного запуска на той же машине?

например

# Python 2.7

import pickle
initial = pickle.dumps({'a': 1, 'b': 2})
for _ in xrange(1000**2):
    assert pickle.dumps({'a': 1, 'b': 2}) == initial

Зависит ли это от фактической структуры моего объекта dict (вложенные значения и т. Д.)?

UPD: Дело в том, что на самом деле я не могу заставить вышеприведенный код потерпеть неудачу в рамках одного запуска (Python 2.7), независимо от того, как выглядит мой объект dict (какие ключи / значения и т. Д.)

Ответы [ 5 ]

0 голосов
/ 26 октября 2018

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

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

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

Далее, наиболее важным фактором в порядке итераций словаря до cPython 3.6 является функция генерации хеш-ключа, которая должна бытьстабильный в течение одного времени жизни исполняемого файла Python (иначе вы бы сломали все словари), поэтому однопроцессный тест никогда не увидит изменения порядка словаря на основе результатов различных хеш-функций.

В настоящее время все версии протокола протравливания хранят данные для словаря в виде потока пар ключ-значение;при загрузке поток декодируется, и пары ключ-значение присваиваются обратно в словарь в порядке на диске, поэтому порядок вставки по крайней мере стабилен с этой точки зрения. НО между различными версиями Python, архитектурами машин и локальной конфигурацией, результаты хеш-функции будут абсолютно разными:

  • Используется PYTHONHASHSEED переменная среды , используемаяв генерации хэшей для ключей str, bytes и datetime.Параметр доступен начиная с Python 2.6.8 и 3.2.3, он включен и установлен по умолчанию random по умолчанию в Python 3.3.Таким образом, настройка варьируется от версии Python до версии Python, и могут быть локально установлены на что-то другое.
  • Хеш-функция создает целое число ssize_t, зависящее от платформы, со знаком целочисленного типа,поэтому разные архитектуры могут создавать разные хэши только потому, что они используют определение типа ssize_t большего или меньшего размера.

С разным выводом хеш-функции от машины к машине и от запуска Python к запуску Python вы будет видеть различные строковые представления словаря.

И, наконец, начиная с cPython 3.6, реализация типа dict изменилась на более компактный формат, который также происходит сохранить порядок ввода.Начиная с Python 3.7, спецификация языка изменилась, чтобы сделать это поведение обязательным, поэтому другие реализации Python должны реализовывать ту же семантику.Таким образом, выборка и выборка между различными реализациями Python или версиями, предшествующими Python 3.7, могут также привести к другому порядку вывода словаря, даже при прочих равных коэффициентах.

0 голосов
/ 26 октября 2018

Как и в случае с разочаровывающе большим количеством вещей в Python, ответ "вроде".Прямо из документов,

Формат сериализации рассола гарантированно будет обратно совместим во всех выпусках Python.

Это потенциально очень незначительно отличается от того, что вы просите.Если сейчас это правильный маринованный словарь, он всегда будет действительным маринованным словарем и всегда десериализуется в правильный словарь.Это оставляет невысказанным несколько свойств, которые вы можете ожидать и которые не должны содержать:

  • Травление не должно быть детерминированным, даже для одного и того же объекта в том же экземпляре Python на том жеПлатформа.В одном и том же словаре может быть бесконечно много возможных зашифрованных представлений (не то, чтобы мы ожидали, что формат когда-либо будет неэффективным для поддержки сколь угодно больших степеней дополнительного заполнения).Как указывают другие ответы, словари не имеют определенного порядка сортировки, и это может дать как минимум n!строковые представления словаря с n элементами.
  • Если продолжить с последней точки, не гарантируется, что pickle является согласованным даже в одном экземпляре Python.На практике эти изменения в настоящее время не происходят, но такое поведение не гарантируется в будущих версиях Python.
  • В будущих версиях Python не требуется сериализовывать словари так, чтобы это было совместимо с текущимиверсии.Единственное обещание, которое мы имеем, состоит в том, что они смогут правильно десериализовать наши словари.В настоящее время словари поддерживаются одинаково во всех форматах Pickle, но это не должно оставаться неизменным (не то чтобы я подозреваю, что это когда-нибудь изменится).
0 голосов
/ 23 октября 2018

Если вы не измените dict, его строковое представление не изменится во время данного запуска программы, а его метод .keys вернет ключи в том же порядке.Однако порядок может меняться от запуска к запуску (до Python 3.6).

Кроме того, два разных объекта dict, имеющие одинаковые пары ключ-значение, не гарантируют использование одного и того же порядка (до Python 3.6).


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

0 голосов
/ 25 октября 2018

Python2 архивы неупорядочены;порядок зависит от значений хеш-ключей, как объяснено в этом великом ответе Мартина Питерса.Я не думаю, что вы можете использовать dict здесь, но вы можете использовать OrderedDict (требуется Python 2.7 или выше), который поддерживает порядок ключей.Например,

from collections import OrderedDict

data = [('b', 0), ('a', 0)]
d = dict(data)
od = OrderedDict(data)

print(d)
print(od)

#{'a': 0, 'b': 0}
#OrderedDict([('b', 0), ('a', 0)])

Вы можете выбрать OrderedDict так, как если бы вы выбрали его, но порядок будет сохранен, и результирующая строка будет такой же при выборе одинаковых объектов.

from collections import OrderedDict
import pickle

data = [('a', 1), ('b', 2)]
od = OrderedDict(data)
s = pickle.dumps(od)
print(s)

Обратите внимание, что вы не должны передавать dict в конструкторе OrderedDict, так как ключи уже были бы помещены.Если у вас есть словарь, вы должны сначала преобразовать его в кортежи с нужным порядком.OrderedDict является подклассом dict и имеет все методы dict, поэтому вы можете создать пустой объект и назначить новые ключи.

Ваш тест не проходит, потому что вы используете ту же версию Python и те же условия - порядок словаря не будет меняться случайным образом между итерациями цикла.Но мы можем продемонстрировать, как ваш код не может генерировать разные строки, когда мы меняем порядок ключей в словаре.

import pickle

initial = pickle.dumps({'a': 1, 'b': 2})
assert pickle.dumps({'b': 2, 'a': 1}) != initial

Результирующая строка должна отличаться, когда мы сначала ставим ключ 'b' (он будет другим в Python> = 3.6), но в Python2 это то же самое, потому что ключ 'a' помещается перед ключом«б».

Чтобы ответить на ваш главный вопрос, словари Python2 не упорядочены, но словарь, вероятно, будет иметь тот же порядок при использовании того же кода и версии Python.Однако этот порядок может не совпадать с порядком, в котором вы поместили элементы в словарь.Если порядок важен, лучше использовать OrderedDict или обновить версию Python.

0 голосов
/ 23 октября 2018

Нет, вы не можете.Это зависит от многих вещей, включая значения ключей, состояние интерпретатора и версию Python.

Если вам нужно согласованное представление, рассмотрите возможность использования JSON с канонической формой.

EDIT

Я не совсем уверен, почему люди отказываются от этого без каких-либо комментариев, но я уточню.

pickle не предназначен для создания надежных представлений, его чистая машина - (не человек -) читаемый сериализатор.

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

Самый простой способ проверить это - выгрузить одни и те же данные в версиях со значительными различиями в обработке структуры и / или заполненииобработка, сохраняя ваши ключи вне кэшированного диапазона (без коротких целых чисел и строк):

Python 3.5.6 (default, Oct 26 2018, 11:00:52) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> d = {'first_string_key': 1, 'second_key_string': 2}
>>> pickle.dump
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x11\x00\x00\x00second_key_stringq\x01K\x02X\x10\x00\x00\x00first_string_keyq\x02K\x01u.'

Python 3.6.7 (default, Oct 26 2018, 11:02:59) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pickle
>>> d = {'first_string_key': 1, 'second_key_string': 2}
>>> pickle.dumps(d)
b'\x80\x03}q\x00(X\x10\x00\x00\x00first_string_keyq\x01K\x01X\x11\x00\x00\x00second_key_stringq\x02K\x02u.'
...