我有一个类MyClass,它包含两个成员变量foo和bar:
class MyClass:
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
我有这个类的两个实例,每个实例都有相同的foo和bar值:
x = MyClass('foo', 'bar')
y = MyClass('foo', 'bar')
然而,当我比较它们是否相等时,Python返回False:
>>> x == y
False
我如何使python认为这两个对象相等?
你应该实现方法__eq__:
class MyClass:
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
def __eq__(self, other):
if not isinstance(other, MyClass):
# don't attempt to compare against unrelated types
return NotImplemented
return self.foo == other.foo and self.bar == other.bar
现在它输出:
>>> x == y
True
注意,实现__eq__将自动使类的实例不可哈希,这意味着它们不能存储在集和字典中。如果你不是建模一个不可变的类型(例如,如果属性foo和bar可能会在你的对象的生命周期内改变值),那么建议把你的实例保留为不可哈希的。
如果你正在建模一个不可变类型,你还应该实现数据模型钩子__hash__:
class MyClass:
...
def __hash__(self):
# necessary for instances to behave sanely in dicts and sets.
return hash((self.foo, self.bar))
一般的解决方案,比如循环__dict__并比较值的想法,是不可取的——它永远不可能真正通用,因为__dict__可能包含不可比较或不可哈希的类型。
注意:在Python 3之前,你可能需要使用__cmp__而不是__eq__。Python 2用户可能还想实现__ne__,因为在Python 2中不会自动创建一个合理的不等式默认行为(即反转相等结果)。
在Python 3.7(及以上版本)中的数据类中,对象实例的相等性比较是一个内置特性。
Python 3.6提供了数据类的后端端口。
(Py37) nsc@nsc-vbox:~$ python
Python 3.7.5 (default, Nov 7 2019, 10:50:52)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from dataclasses import dataclass
>>> @dataclass
... class MyClass():
... foo: str
... bar: str
...
>>> x = MyClass(foo="foo", bar="bar")
>>> y = MyClass(foo="foo", bar="bar")
>>> x == y
True
我写了这个,并把它放在我的项目中的test/utils模块中。对于情况下,当它不是一个类,只是计划ol' dict,这将遍历两个对象,并确保
每个属性都与其对应的属性相等
不存在悬空属性(只存在于一个对象上的attrs)
它的大…这一点都不性感……但是,哦,boi,它工作!
def assertObjectsEqual(obj_a, obj_b):
def _assert(a, b):
if a == b:
return
raise AssertionError(f'{a} !== {b} inside assertObjectsEqual')
def _check(a, b):
if a is None or b is None:
_assert(a, b)
for k,v in a.items():
if isinstance(v, dict):
assertObjectsEqual(v, b[k])
else:
_assert(v, b[k])
# Asserting both directions is more work
# but it ensures no dangling values on
# on either object
_check(obj_a, obj_b)
_check(obj_b, obj_a)
您可以通过删除_assert并使用普通的ol' assert来稍微清理它,但当它失败时,您得到的消息是非常没有帮助的。