在编写自定义类时,允许使用==和!=操作符进行等价通常是很重要的。在Python中,这可以通过分别实现__eq__和__ne__特殊方法实现。我发现最简单的方法是以下方法:
class Foo:
def __init__(self, item):
self.item = item
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
你知道更优雅的方法吗?你知道使用上面比较__dict__的方法有什么特别的缺点吗?
注意:一点澄清——当__eq__和__ne__未定义时,你会发现这样的行为:
>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
False
也就是说,a == b的计算结果为False,因为它实际上运行的是a is b,这是一个身份测试(即,“a和b是同一个对象吗?”)。
当__eq__和__ne__被定义时,你会发现这样的行为(这是我们所追求的行为):
>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
True
我用__ne__的默认实现写了一个自定义基,它简单地对__eq__求反:
class HasEq(object):
"""
Mixin that provides a default implementation of ``object.__neq__`` using the subclass's implementation of ``object.__eq__``.
This overcomes Python's deficiency of ``==`` and ``!=`` not being symmetric when overloading comparison operators
(i.e. ``not x == y`` *does not* imply that ``x != y``), so whenever you implement
`object.__eq__ <https://docs.python.org/2/reference/datamodel.html#object.__eq__>`_, it is expected that you
also implement `object.__ne__ <https://docs.python.org/2/reference/datamodel.html#object.__ne__>`_
NOTE: in Python 3+ this is no longer necessary (see https://docs.python.org/3/reference/datamodel.html#object.__ne__)
"""
def __ne__(self, other):
"""
Default implementation of ``object.__ne__(self, other)``, delegating to ``self.__eq__(self, other)``.
When overriding ``object.__eq__`` in Python, one should also override ``object.__ne__`` to ensure that
``not x == y`` is the same as ``x != y``
(see `object.__eq__ <https://docs.python.org/2/reference/datamodel.html#object.__eq__>`_ spec)
:return: ``NotImplemented`` if ``self.__eq__(other)`` returns ``NotImplemented``, otherwise ``not self.__eq__(other)``
"""
equal = self.__eq__(other)
# the above result could be either True, False, or NotImplemented
if equal is NotImplemented:
return NotImplemented
return not equal
如果你继承了这个基类,你只需要实现__eq__和基类。
回想起来,更好的方法可能是将其作为装饰器实现。比如@functools. total_orders