如何在没有订单的情况下使用dicts断言两个列表?

时间:2016-04-28 18:24:21

标签: python unit-testing python-3.x

大家。我最近从python 2切换到3.5.1并且有一个断言函数,我无法重写。

def assertEqualUnordered(self, data1, data2):
    """
    compare that data are similar
    i.e.:
    [d1, d2] == [d2, d1]
    or
    {'a': [d1, d2]} == {'a': [d2, d1]}
    or
    [{'a': [d1, d2]}, {'b': [d3, d4]}] == [{'b': [d4, d3]}, {'a': [d2, d1]}]
    """
    if isinstance(data1, list) or isinstance(data1, tuple):
        self.assertEqual(len(data1), len(data2))
        for d1, d2 in zip(sorted(data1), sorted(data2)):
            self.assertEqualUnordered(d1, d2)
    elif isinstance(data1, dict):
        data1_keys = sorted(data1.keys())
        data2_keys = sorted(data2.keys())
        self.assertListEqual(data1_keys, data2_keys)
        for key in data1_keys:
            self.assertEqualUnordered(data1[key], data2[key])
    else:
        self.assertEqual(data1, data2)

一般来说,这段代码工作正常,但如果d1和d2是dicts,那么比我有:

TypeError: unorderable types: dict() < dict()

如何重写它以在py3k中工作?

编辑1: 简化代码示例:

def assertEqualUnordered(data1, data2):
    assert len(data1) == len(data2)
    for d1, d2 in zip(sorted(data1), sorted(data2)):
        assert d1 == d2

data1 = [{'a': 'a'}, {'b': 'b'}]
data2 = [{'b': 'b'}, {'a': 'a'}]
assertEqualUnordered(data1, data2)

1 个答案:

答案 0 :(得分:4)

我不确定这是否是最简单的方法,但你可以通过复活Python 3删除的cmp函数来完成排序。这些方面的东西:

def cmp(lhs, rhs):
    try:
        if lhs == rhs:
            return 0
        elif lhs < rhs:
            return -1
        else:
            return 1
    except TypeError:
        if isinstance(lhs, dict) and isinstance(rhs, dict):
            return dict_cmp(lhs, rhs)
        raise

有关dict_cmp的实施,请参阅Is there a description of how __cmp__ works for dict objects in Python 2?

获得cmp功能后,您可以执行sorted(data1, key = functools.cmp_to_key(cmp))

这仍然没有完成,因为我还没有试图涵盖混合型比较,例如,如果您将[{'a' : 'b'}, ['a']]作为其中一个对象传入,则会出现这种比较。但希望我已经提供了进入的方向。

另一种方法是回退到O(n^2)算法,这实际上符合Python3的观点,即词典没有订单。不是对列表进行排序然后将它们进行相等比较,而是依次取左边的每个项目并在右边搜索一个等于它的项目(删除后确保两边的计数相同,因为你不要错误地声称[x, x, y]等于[x, y, y])。

顺便说一下,答案并不是真的有必要,但我注意到你当前的代码会说['a', 'b']等于{'a' : 'foo', 'b' : 'bar'}无序,但只有当列表在左边并且字典在对。如果您以相反的方式传递它们,则当代码尝试在列表上调用keys()时会出现异常。这可能需要解决,具体取决于您计划传入的内容; - )