Python 2 assertItemsEqual 结果不正确
Python 2 assertItemsEqual incorrect result
我运行在Python2下使用unittest.TestCase.assertItemsEqual
函数进入一个有趣的情况;在这里发布我的发现以供后代使用。
以下单元测试在 Python 2 应该成功时中断:
import unittest
class Foo(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
def __repr__(self):
return '({},{})'.format(self.a, self.b)
def __eq__(self, other):
return self.a == other.a and self.b == other.b
def __lt__(self, other):
return (self.a, self.b) < (other.a, other.b)
class Test(unittest.TestCase):
def test_foo_eq(self):
self.assertEqual(sorted([Foo()]), sorted([Foo()]))
self.assertItemsEqual([Foo()], [Foo()])
unittest.main()
这是输出:
======================================================================
FAIL: test_foo_eq (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/tsanders/scripts/one_offs/test_unittest_assert_items_equal2.py", line 17, in test_foo_eq
self.assertItemsEqual([Foo()], [Foo()])
AssertionError: Element counts were not equal:
First has 1, Second has 0: (1,2)
First has 0, Second has 1: (1,2)
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)
这很令人困惑,因为 docs 状态:
It [assertItemsEqual
] is the equivalent of assertEqual(sorted(expected), sorted(actual))
but it works with sequences of unhashable objects as well.
相同的测试在 Python 3 下通过(将 self.assertItemsEqual
替换为 self.assertCountEqual
,因为名称已更改)。
编辑:发布这个问题后,我确实找到了 this 其他问题,涵盖了既没有定义 __eq__
也没有定义 __hash__
的情况。
为了让测试通过 Python 2 和 3,我必须将行 __hash__ = None
添加到 Foo
。
assertItemsEqual
/assertCountEqual
函数采用不同的代码路径,具体取决于每个列表中的项目是否可散列。并根据 docs:
If a class does not define a __cmp__()
or __eq__()
method it should not define a __hash__()
operation either; if it defines __cmp__()
or __eq__()
but not __hash__()
, its instances will not be usable in hashed collections.
考虑到这一点,当定义 __eq__
时,Python 2 和 3 对于 __hash__
有不同的行为:
- 在 Python 2 中,定义
__eq__
不会影响默认提供的 __hash__
,但尝试在散列容器中使用该项目会导致实现定义的行为(例如键可能会重复,因为它们具有不同的哈希值,即使 __eq__
会 return True
).
- 在Python 3中,定义
__eq__
将__hash__
设置为None
。
从 Python 2.6 开始,__hash__
可以显式设置为 None
以使 class 不可散列。在我的例子中,需要才能让assertItemsEqual
使用依赖于__eq__
而不是__hash__
的正确比较算法。
我运行在Python2下使用unittest.TestCase.assertItemsEqual
函数进入一个有趣的情况;在这里发布我的发现以供后代使用。
以下单元测试在 Python 2 应该成功时中断:
import unittest
class Foo(object):
def __init__(self, a=1, b=2):
self.a = a
self.b = b
def __repr__(self):
return '({},{})'.format(self.a, self.b)
def __eq__(self, other):
return self.a == other.a and self.b == other.b
def __lt__(self, other):
return (self.a, self.b) < (other.a, other.b)
class Test(unittest.TestCase):
def test_foo_eq(self):
self.assertEqual(sorted([Foo()]), sorted([Foo()]))
self.assertItemsEqual([Foo()], [Foo()])
unittest.main()
这是输出:
======================================================================
FAIL: test_foo_eq (__main__.Test)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/tsanders/scripts/one_offs/test_unittest_assert_items_equal2.py", line 17, in test_foo_eq
self.assertItemsEqual([Foo()], [Foo()])
AssertionError: Element counts were not equal:
First has 1, Second has 0: (1,2)
First has 0, Second has 1: (1,2)
----------------------------------------------------------------------
Ran 1 test in 0.000s
FAILED (failures=1)
这很令人困惑,因为 docs 状态:
It [
assertItemsEqual
] is the equivalent ofassertEqual(sorted(expected), sorted(actual))
but it works with sequences of unhashable objects as well.
相同的测试在 Python 3 下通过(将 self.assertItemsEqual
替换为 self.assertCountEqual
,因为名称已更改)。
编辑:发布这个问题后,我确实找到了 this 其他问题,涵盖了既没有定义 __eq__
也没有定义 __hash__
的情况。
为了让测试通过 Python 2 和 3,我必须将行 __hash__ = None
添加到 Foo
。
assertItemsEqual
/assertCountEqual
函数采用不同的代码路径,具体取决于每个列表中的项目是否可散列。并根据 docs:
If a class does not define a
__cmp__()
or__eq__()
method it should not define a__hash__()
operation either; if it defines__cmp__()
or__eq__()
but not__hash__()
, its instances will not be usable in hashed collections.
考虑到这一点,当定义 __eq__
时,Python 2 和 3 对于 __hash__
有不同的行为:
- 在 Python 2 中,定义
__eq__
不会影响默认提供的__hash__
,但尝试在散列容器中使用该项目会导致实现定义的行为(例如键可能会重复,因为它们具有不同的哈希值,即使__eq__
会 returnTrue
). - 在Python 3中,定义
__eq__
将__hash__
设置为None
。
从 Python 2.6 开始,__hash__
可以显式设置为 None
以使 class 不可散列。在我的例子中,需要才能让assertItemsEqual
使用依赖于__eq__
而不是__hash__
的正确比较算法。