2016-01-21 171 views
5

我注意到了Python 3的json.dumps实现中的一些奇怪行为,即每次将相同的对象从执​​行转储到执行时,键的顺序都会改变。谷歌搜索没有工作,因为我不关心排序键,我只是希望他们保持不变!下面是一个示例脚本:如何保持用Python 3 json.dumps修复JSON键顺序?

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漏洞。为了防止它,一台PC上的相同散列码在另一台PC上应该不同。

由于兼容性原因,Python 2可能已经禁用了此行为(哈希随机化),因为这会例如破坏doctests。 Python 3可能(假设)不需要可兼容性。

相关问题