Python中字典的严格比较

时间:2017-08-25 19:29:02

标签: python python-3.x dictionary nested equality

我在比较两个相似的词典时遇到了一些麻烦。我想更严格地比较值(可能还有键)。

这是真正基本的问题:

>>> {'a': True} == {'a': 1}
True

同样(并且有些令人困惑):

>>> {1: 'a'} == {True: 'a'}
True

这是有道理的,因为True == 1。我正在寻找的东西更像is,但比较两个可能嵌套的词典。显然你不能在两个字典上使用is,因为即使所有元素都相同,它也总会返回False

我目前的解决方案是使用json.dumps来获取两者的字符串表示并进行比较。

>>> json.dumps({'a': True}, sort_keys=True) == json.dumps({'a': 1}, sort_keys=True)
False

但这只适用于所有内容都是JSON可序列化的。

我还尝试手动比较所有键和值:

>>> l = {'a': True}
>>> r = {'a': 1}
>>> r.keys() == l.keys() and all(l[key] is r[key] for key in l.keys())
False

但如果字典有一些嵌套结构,则会失败。我想我可以写一个递归版本来处理嵌套的情况,但它似乎不必要的丑陋和非pythonic。

是否有“标准”或简单的方法?

谢谢!

4 个答案:

答案 0 :(得分:2)

你非常接近JSON:使用Python的pprint模块代替。记录这些用于在Python 2.5+3中对字典进行排序:

在计算显示之前,字典按键排序。

让我们确认一下。这是Python 3.6中的一个会话(即使对于常规dict对象,它也可以方便地保留插入顺序):

Python 3.6.2 (v3.6.2:5fd33b5, Jul  8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)]
 on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> a = {2: 'two', 3: 'three', 1: 'one'}
>>> b = {3: 'three', 2: 'two', 1: 'one'}
>>> a
{2: 'two', 3: 'three', 1: 'one'}
>>> b
{3: 'three', 2: 'two', 1: 'one'}
>>> a == b
True
>>> c = {2: 'two', True: 'one', 3: 'three'}
>>> c
{2: 'two', True: 'one', 3: 'three'}
>>> a == b == c
True
>>> from pprint import pformat
>>> pformat(a)
"{1: 'one', 2: 'two', 3: 'three'}"
>>> pformat(b)
"{1: 'one', 2: 'two', 3: 'three'}"
>>> pformat(c)
"{True: 'one', 2: 'two', 3: 'three'}"
>>> pformat(a) == pformat(b)
True
>>> pformat(a) == pformat(c)
False
>>>

让我们快速确认漂亮打印对嵌套词典进行排序:

>>> a['b'] = b
>>> a
{2: 'two', 3: 'three', 1: 'one', 'b': {3: 'three', 2: 'two', 1: 'one'}}
>>> pformat(a)
"{1: 'one', 2: 'two', 3: 'three', 'b': {1: 'one', 2: 'two', 3: 'three'}}"
>>>

因此,不是序列化为JSON,而是使用pprint.pformat()进行序列化。我想可能会有一些角落情况,你想要考虑的两个对象不相等,但仍会创建相同的漂亮打印表示。但这些情况应该是罕见的,你想要一些简单的Pythonic,这就是。

答案 1 :(得分:0)

您可以使用isinstance()在常规字典条目和嵌套字典条目之间进行描述。通过这种方式,您可以使用is进行迭代以进行严格比较,还可以检查何时需要将某个级别下载到嵌套字典中。

https://docs.python.org/3/library/functions.html#isinstance

myDict = {'a': True, 'b': False, 'c': {'a': True}}
for key, value in myDict.items():
    if isinstance(value, dict):
        # do what you need to do....
    else:
        # etc...

答案 2 :(得分:0)

您可以逐个元素地测试所有(键,值)对的标识:

def equal_dict(d1, d2):
    return all((k1 is k2) and (v1 is v2)
               for (k1, v1), (k2, v2) in zip(d1.items(), d2.items()))

>>> equal_dict({True: 'a'}, {True: 'a'})
True

>>> equal_dict({1: 'a'}, {True: 'a'})
False

这适用于floatintstrbool,但不适用于其他序列或更复杂的对象。 无论如何,如果你需要,这是一个开始。

答案 3 :(得分:0)

我认为你正在寻找这样的东西。但是,由于您没有提供示例数据,我不会猜测它可能是什么

from boltons.itertools import remap

def compare(A, B): return A == B and type(A) == type(B)

dict_to_compare_against = { some dict }

def visit(path, key, value):
    cur = dict_to_compare_against
    for i in path:
        cur = cur[i]

    if not compare(cur, value):
        raise Exception("Not equal")


remap(other_dict, visit=visit)