大家。我最近从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)
答案 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()
时会出现异常。这可能需要解决,具体取决于您计划传入的内容; - )