__lt__而不是__cmp__

96 python operator-overloading

Python 2.x有两种方法可以重载比较运算符,__cmp__或者"丰富的比较运算符",如__lt__. 丰富的比较超载被认为是首选,但为什么会这样呢?

丰富的比较运算符更容易实现每个,但您必须使用几乎相同的逻辑实现其中几个.但是,如果你可以使用内置cmp和元组排序,那么__cmp__变得非常简单并完成所有的比较:

class A(object):
  def __init__(self, name, age, other):
    self.name = name
    self.age = age
    self.other = other
  def __cmp__(self, other):
    assert isinstance(other, A) # assumption for this example
    return cmp((self.name, self.age, self.other),
               (other.name, other.age, other.other))
Run Code Online (Sandbox Code Playgroud)

这种简单性似乎比重载所有6(!)丰富的比较更好地满足了我的需求.(但是,如果你依赖于"交换的论证"/反映的行为,你可以把它归结为"只是"4,但这导致并发症的净增加,在我的拙见中.)

如果我只是超载,是否有任何不可预见的陷阱需要注意__cmp__

我明白了<,<=,==等运营商也可以被重载用于其他目的,并且可以返回任何他们喜欢的对象.我不是在询问这种方法的优点,而是仅仅考虑使用这些运算符进行比较时的差异,这与它们对数字的意义相同.

更新:克里斯托弗指出,cmp正在消失3.x. 有没有其他方法可以使实施比较变得如上所述__cmp__

Ale*_*lli 87

是的,很容易实现一切,例如__lt__使用mixin类(或元类,或类装饰器,如果你的味道以这种方式运行).

例如:

class ComparableMixin:
  def __eq__(self, other):
    return not self<other and not other<self
  def __ne__(self, other):
    return self<other or other<self
  def __gt__(self, other):
    return other<self
  def __ge__(self, other):
    return not self<other
  def __le__(self, other):
    return not other<self
Run Code Online (Sandbox Code Playgroud)

现在你的类可以__lt__从ComparableMixin 定义just 和multiply继承(在它需要的任何其他基础之后,如果有的话).类装饰器非常相似,只是插入类似的函数作为它正在装饰的新类的属性(结果可能在运行时在显微镜下更快,在内存方面同样微小的成本).

当然,如果你的类必须实现(例如)一些特别快速的方法__eq____ne__,就应该直接定义里面,这样的mixin的版本不使用(例如,是的情况下dict) -事实上__ne__很可能会被定义为方便那样:

def __ne__(self, other):
  return not self == other
Run Code Online (Sandbox Code Playgroud)

但在上面的代码中,我想保持令人愉悦的对称性,只使用<;-).至于为什么__cmp__不得不去,因为我们确实__lt__和朋友一起,为什么要采用另一种不同的方式来做同样的事情呢?它在每个Python运行时(Classic,Jython,IronPython,PyPy,......)中都非常重要.该代码绝对不会有虫子的是不存在的代码-那里Python的原则是,应该是一个理想的执行任务(C具有相同的原则,在"C的精神"的部分明显的方式ISO标准,顺便说一句.

这并不意味着我们走出去的路,禁止的东西(例如,混入以及一些应用类装饰之间近乎等价),但绝对意味着我们不喜欢周围的代码进行的编译器和/或冗余存在的运行时只是为了支持多个等效方法来执行完全相同的任务.

进一步编辑:实际上有更好的方法为许多类提供比较和散列,包括问题中的__key__方法- 正如我在对问题的评论中提到的那样.因为我从来没有为它编写PEP,所以如果你喜欢的话,你当前必须使用Mixin(&c)来实现它:

class KeyedMixin:
  def __lt__(self, other):
    return self.__key__() < other.__key__()
  # and so on for other comparators, as above, plus:
  def __hash__(self):
    return hash(self.__key__())
Run Code Online (Sandbox Code Playgroud)

这是一个实例与其他实例比较,一个很常见的情况归结为比较每个有几个字段元组-然后,哈希应该完全相同的基础上实施.的__key__直接需要特殊的方法解决.

  • 使用`<`在Python 3中实现`__eq__`是非常糟糕的主意,因为'TypeError:unorderable types`. (4认同)
  • 在 Python 2.7+/3.2+ 中,您可以使用 `functools.total_ordering` 而不是构建自己的 `ComparableMixim`。正如 [jmagnusson 的回答](http://stackoverflow.com/a/10312447/445073) 中所建议的 (3认同)

jma*_*son 47

为了简化这种情况,Python 2.7 +/3.2 +中有一个类装饰器,functools.total_ordering,可用于实现Alex建议的内容.来自文档的示例:

@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()))
Run Code Online (Sandbox Code Playgroud)

  • `total_ordering`虽然没有实现`__ne__`,所以要小心! (8认同)
  • @Flimm,它没有,但是`__ne__`.但那是因为`__ne__`具有委托给`__eq__`的默认实现.所以这里没有什么值得关注的. (3认同)