Python 中词典的严格比较

Strict Comparison of Dictionaries in Python

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

这是真正的基本问题:

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

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

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

这是有道理的,因为 True == 1。我正在寻找的是行为更像 is 的东西,但比较两个可能嵌套的字典。显然你不能在两个字典上使用 use is,因为它总是 return 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。

是否有 "standard" 或简单的方法来做到这一点?

谢谢!

您可以使用 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...

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

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,但不适用于其他序列或更复杂的对象。 不管怎样,如果你需要的话,这是一个开始。

您与 JSON 非常接近:改用 Python 的 pprint 模块。这被记录为在 Python 2.5+ and 3:

中对字典进行排序
Dictionaries are sorted by key before the display is computed.

让我们确认一下。这是 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 的东西,这就是。

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

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)