2016-01-21 3 views
5

Я заметил некоторое странное поведение при реализации Python 3 json.dumps, а именно: порядок ключей изменяется каждый раз, когда я выгружаю один и тот же объект из исполнения в исполнение. Googling не работал, так как я не забочусь о сортировке ключей, я просто хочу, чтобы они остались прежними! Ниже приведен пример сценария:Как сохранить порядок ключей JSON с Python 3 json.dumps?

import json 

data = { 
    'number': 42, 
    'name': 'John Doe', 
    'email': '[email protected]', 
    'balance': 235.03, 
    'isadmin': False, 
    'groceries': [ 
     'apples', 
     'bananas', 
     'pears', 
    ], 
    'nested': { 
     'complex': True, 
     'value': 2153.23412 
    } 
} 

print(json.dumps(data, indent=2)) 

Когда я запускаю этот скрипт я получаю разные выходы каждый раз, когда, например:

$ python print_data.py 
{ 
    "groceries": [ 
    "apples", 
    "bananas", 
    "pears" 
    ], 
    "isadmin": false, 
    "nested": { 
    "value": 2153.23412, 
    "complex": true 
    }, 
    "email": "[email protected]", 
    "number": 42, 
    "name": "John Doe", 
    "balance": 235.03 
} 

Но потом запустить его снова, и я получаю:

$ python print_data.py 
{ 
    "email": "[email protected]", 
    "balance": 235.03, 
    "name": "John Doe", 
    "nested": { 
    "value": 2153.23412, 
    "complex": true 
    }, 
    "isadmin": false, 
    "groceries": [ 
    "apples", 
    "bananas", 
    "pears" 
    ], 
    "number": 42 
} 

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

Любая идея, что происходит? Как это исправить? Обратите внимание, что я бы хотел избежать использования OrderedDict или выполнения любой сортировки, и важно то, что строковое представление остается неизменным между исполнениями. Также это предназначено только для тестирования и не влияет на реализацию моего модуля.

+0

Я могу гарантировать, что единственная причина, по которой заказ зафиксирован на Python 2, является случайным, если только 'sort_keys = True' –

+0

@WayneWerner это не случайно; хэш-функции детерминированы - см. комментарии ниже, изменения порядка после Python 3.3 из-за включения случайного хэш-семени. – bbengfort

+0

Ну, я стою исправлено! Очень интересно. –

ответ

8

Python словари и объекты JSON: неупорядоченный. Вы можете спросить json.dumps() сортировать ключи на выходе; это предназначено для облегчения тестирования. Используйте параметр sort_keys для True:

print(json.dumps(data, indent=2, sort_keys=True)) 

См Why is the order in Python dictionaries and sets arbitrary?, почему вы видите различный порядок каждый раз.

Вы можете установить PYTHONHASHSEED environment variable на целочисленное значение, чтобы «заблокировать» порядок словаря; используйте это только для запуска тестов, а не для производства, так как цель хэш-рандомизации состоит в том, чтобы предотвратить атакующую от тривиальной DOS-программы.

+0

С сообщением, которое вы связали, это то, что я искал: «Обратите внимание, что с Python 3.3 также используется случайное хеш-семя, что делает непредвиденные столкновения с хэшем для предотвращения определенных типов отказа в обслуживании (если злоумышленник не реагирует на Python-сервер, вызывая массовые хеш-коллизии). Это означает, что порядок данного словаря также зависит от случайного хеш-семени для текущего вызова Python ». – bbengfort

+0

Знаете ли вы, как исправить семена хеша для целей тестирования? Мои тесты требуют, чтобы я не передавал дополнительные аргументы в функцию json.dumps. – bbengfort

+2

@bbengfort: вы можете установить переменную среды '' PYTHONHASHSEED' (https://docs.python.org/3/using/cmdline.html#envvar-PYTHONHASHSEED) на целочисленное значение. –

0

История такого поведения this Уязвимость. Чтобы предотвратить это, одинаковые хэш-коды на одном ПК должны отличаться друг от друга.

Python 2, вероятно, отключил это поведение (хеш-рандомизация) по умолчанию из-за совместимости, поскольку это, например, приведет к расколу. Вероятно, Python 3 (предположение) не нуждался в компиляции.

Смежные вопросы