Python中字典的严格比较

Strict Comparison of Dictionaries in Python

提问人:John P 提问时间:8/26/2017 更新时间:10/23/2019 访问量:1292

问:

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

这是真正基本的问题:

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

类似地(有点令人困惑):

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

这是有道理的,因为.我正在寻找的是行为更像的东西,但比较了两个可能的嵌套字典。显然,您不能在两个字典上使用 use,因为即使所有元素都相同,它也会始终返回 。True == 1isisFalse

我目前的解决方案是只用于获取两者的字符串表示并进行比较。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

但是,如果字典具有一些嵌套结构,则此操作将失败。我想我可以写一个递归版本来处理嵌套的情况,但它似乎不必要地丑陋且不合时宜。

有没有“标准”或简单的方法来做到这一点?

谢谢!

python-3.x 字典 嵌套相

评论

4赞 jonrsharpe 8/26/2017
这似乎不是一个好主意 - 作为有效标识符的小整数和字符串可以与之比较,因为它们是内部的(至少在 CPython 中),但您很快就会遇到任何非平凡的问题。你试图解决的根本问题是什么,导致你与。 作为字典键?isTrue1
0赞 Alter 8/26/2017
一些包含您要查找的嵌套大小写的示例词典会很好
0赞 John P 8/26/2017
我在 Django 中存储了一些非规范化数据。(这是产品数据)。明确表示产品需要组装,但不太清楚。DeepDiff 看起来会起作用。只需要做一个.JSONField{"assembly_required": True}{"assembly_required": 1}DeepDiff(left, right) == {}
1赞 John Y 8/26/2017
嗯,不知道为什么提到 DeepDiff 的原始评论被删除了,但 OP 并没有凭空扯出来:stackoverflow.com/a/26171760/95852

答:

0赞 blake 8/26/2017 #1

可用于在常规词典条目和嵌套词典条目之间进行界定。这样一来,你就可以遍历 using 来严格比较,但也可以检查何时需要向下潜入嵌套字典。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...

评论

0赞 Alter 8/26/2017
isinstance(myDict, dict):..myDict -> myDict[x] ?
0赞 blake 8/26/2017
固定。谢谢你的捕捉
0赞 Alter 8/26/2017
我认为这也行不通,x 只是他们的关键。可能需要myDict[x]来获取内容
0赞 pppery 8/26/2017
或使用for x, y in myDict.items()
0赞 blake 8/26/2017
对了,忘了python字典迭代是如何工作的......不好的答案。让我再编辑一次。
1赞 FabienP 8/26/2017 #2

您可以按元素测试所有(键、值)对的身份:

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

评论

0赞 John Y 8/26/2017
这是行不通的,因为您正在使用 .由于实习和可能的其他 Python 微优化,您的测试恰好给出了预期的结果。但它可能会错误地将两个相等的词典报告为不相等。例如,使用两个单独的变量,而不是 ,并为这些变量提供相等但未嵌入的值,例如大整数。is'a'
0赞 John Y 8/26/2017
而且,正如您已经说过的,它不适用于“序列或更复杂的对象”。因此,它不符合 OP 处理嵌套结构的要求。
2赞 John Y 8/26/2017 #3

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

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

让我们确认这一点。下面是 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,而是使用 .我想可能有一些极端情况,你想认为不相等的两个对象仍然会创建相同的漂亮印刷表示。但这些情况应该很少见,你想要一些简单和 Pythonic 的东西,就是这样。pprint.pformat()

0赞 Joonatan Samuel 8/26/2017 #4

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

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)