__lt__ instead of __cmp__
python 2.x有两种方法来重载比较运算符,即
富比较运算符实现每一个都比较简单,但必须用几乎相同的逻辑实现其中的几个。但是,如果您可以使用内置的
1 2 3 4 5 6 7 8 9 | 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)) |
这种简单性似乎比超载所有6个更能满足我的需求!丰富的对比。(然而,如果你依赖于"交换的论点"/反映的行为,你可以把它降到"仅仅"4,但在我看来,这会导致并发症的净增加。)
如果我只是超载了dOCx1(0),我是否需要意识到一些不可预见的陷阱?
我了解
更新:正如克里斯托弗指出的那样,
是的,用一个混合类(或者一个元类,或者一个类修饰器,如果你的口味是这样的话)来实现所有东西是很容易的。
例如:
1 2 3 4 5 6 7 8 9 10 11 | 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 |
现在您的类可以只定义
当然,如果您的类有一些特别快速的方法来实现(例如
1 2 | def __ne__(self, other): return not self == other |
但在上面的代码中,我希望保持仅使用
这并不意味着我们会不遗余力地禁止某些事情(例如,在某些情况下,mixin和类修饰器之间几乎是等价的),但这确实意味着我们不喜欢在编译器和/或运行时携带冗余的代码,而这些代码只支持多个等价的方法来执行完全相同的任务。
进一步编辑:实际上还有更好的方法可以为许多类提供比较和散列,包括问题中的类——
1 2 3 4 5 6 | 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__()) |
将实例与其他实例的比较归结为将每个实例的元组与几个字段进行比较是非常常见的情况——然后,哈希应该在完全相同的基础上实现。
为了简化这个例子,在python 2.7+/3.2+、functools.total_ordering中有一个类修饰符,可以用来实现Alex的建议。文档示例:
1 2 3 4 5 6 7 8 | @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())) |
PEP 207-丰富的比较涵盖了这一点。
另外,在python 3.0中,
受Alex Martelli的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class ComparableMixin(object): """mixin which implements rich comparison operators in terms of a single _compare_to() helper""" def _compare_to(self, other): """return keys to compare self to other. if self and other are comparable, this function should return ``(self key, other key)``. if they aren't, it should return ``None`` instead. """ raise NotImplementedError("_compare_to() must be implemented by subclass") def __eq__(self, other): keys = self._compare_to(other) return keys[0] == keys[1] if keys else NotImplemented def __ne__(self, other): return not self == other def __lt__(self, other): keys = self._compare_to(other) return keys[0] < keys[1] if keys else NotImplemented def __le__(self, other): keys = self._compare_to(other) return keys[0] <= keys[1] if keys else NotImplemented def __gt__(self, other): keys = self._compare_to(other) return keys[0] > keys[1] if keys else NotImplemented def __ge__(self, other): keys = self._compare_to(other) return keys[0] >= keys[1] if keys else NotImplemented |
(编辑6/17/17,考虑到评论。)
我尝试了上面类似的混合答案。我遇到了"没有"的麻烦。这里是一个修改过的版本,它处理与"无"的相等比较。(我认为没有理由费心进行不平等的比较,因为没有理由缺乏语义):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | class ComparableMixin(object): def __eq__(self, other): if not isinstance(other, type(self)): return NotImplemented else: return not self<other and not other<self def __ne__(self, other): return not __eq__(self, other) def __gt__(self, other): if not isinstance(other, type(self)): return NotImplemented else: return other<self def __ge__(self, other): if not isinstance(other, type(self)): return NotImplemented else: return not self<other def __le__(self, other): if not isinstance(other, type(self)): return NotImplemented else: return not other<self |