在编写自定义类时,允许使用==和!=操作符进行等价通常是很重要的。在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

当前回答

你需要小心继承:

>>> class Foo:
    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.__dict__ == other.__dict__
        else:
            return False

>>> class Bar(Foo):pass

>>> b = Bar()
>>> f = Foo()
>>> f == b
True
>>> b == f
False

更严格地检查类型,像这样:

def __eq__(self, other):
    if type(other) is type(self):
        return self.__dict__ == other.__dict__
    return False

除此之外,你的方法也可以很好地工作,这就是特殊方法的用途。

其他回答

我用__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

你不必同时覆盖__eq__和__ne__,你可以只覆盖__cmp__,但这将对==,!==,<,>等的结果产生影响。

测试对象标识。这意味着当a和b都持有对同一对象的引用时,a是b将为True。在python中,你总是在变量中保存一个对象的引用,而不是实际的对象,所以本质上,a是b,它们中的对象应该位于相同的内存位置。最重要的是,你要如何覆盖这种行为?

编辑:我不知道__cmp__已从python 3中删除,因此请避免使用它。

从这个答案:https://stackoverflow.com/a/30676267/541136我已经证明,虽然它是正确的定义__ne__术语__eq__ -而不是

def __ne__(self, other):
    return not self.__eq__(other)

你应该使用:

def __ne__(self, other):
    return not self == other

你需要小心继承:

>>> class Foo:
    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.__dict__ == other.__dict__
        else:
            return False

>>> class Bar(Foo):pass

>>> b = Bar()
>>> f = Foo()
>>> f == b
True
>>> b == f
False

更严格地检查类型,像这样:

def __eq__(self, other):
    if type(other) is type(self):
        return self.__dict__ == other.__dict__
    return False

除此之外,你的方法也可以很好地工作,这就是特殊方法的用途。

这不是一个直接的回答,但似乎有足够的相关性,因为它有时省去了一些冗长乏味的内容。直接从医生那里剪下来的…


functools.total_ordering (cls)

给定一个定义了一个或多个丰富比较排序方法的类,该类装饰器提供其余部分。这简化了指定所有可能的富比较操作所涉及的工作:

的类必须定义一个__lt__ (), __le__ (), __gt__(),或__ge__()。此外,该类应该提供一个__eq__()方法。

2.7新版功能

@total_ordering
class Student:
    def __eq__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))
    def __lt__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))