【问题标题】:Why can't I use the method __cmp__ in Python 3 as for Python 2?为什么我不能像 Python 2 那样在 Python 3 中使用方法 __cmp__?
【发布时间】:2011-11-26 07:27:29
【问题描述】:

下面这段代码

class point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def dispc(self):
        return ('(' + str(self.x) + ',' + str(self.y) + ')')

    def __cmp__(self, other):
        return ((self.x > other.x) and (self.y > other.y))

在 Python 2 中运行良好,但在 Python 3 中出现错误:

>>> p=point(2,3)
>>> q=point(3,4)
>>> p>q
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unorderable types: point() > point()

它仅适用于==!=

【问题讨论】:

  • __cmp__ 在 Python 2 中已经被破坏了。

标签: python python-3.x python-2.x partial-ordering


【解决方案1】:

您需要在Python 3中提供丰富的排序比较方法,分别是__lt____gt____le____ge____eq____ne__。另见:PEP 207 -- Rich Comparisons

__cmp__不再使用了。


更具体地说,__lt__selfother 作为参数,需要返回self 是否小于other。例如:

class Point(object):
    ...
    def __lt__(self, other):
        return ((self.x < other.x) and (self.y < other.y))

(这不是一个明智的比较实现,但很难说出你想要什么。)

所以如果你有以下情况:

p1 = Point(1, 2)
p2 = Point(3, 4)

p1 < p2

这将等同于:

p1.__lt__(p2)

这将返回True

如果点数相等,__eq__ 将返回 True,否则返回 False。其他方法类似。


如果你使用 functools.total_ordering 装饰器,你只需要实现例如__lt____eq__ 方法:

from functools import total_ordering

@total_ordering
class Point(object):
    def __lt__(self, other):
        ...

    def __eq__(self, other):
        ...

【讨论】:

  • 现在您需要定义__hash__ 以在集合中或作为字典键使用您的对象。
  • 感谢您提供有关使用functools.total_ordering 的提示!我只是在 Python 3.6 上试了一下,我只需要定义 __lt__(self, other) 方法。 (可能是因为如果a &lt; bb &lt; a 都是假的,那么a == b 一定是真的。)
  • 它实际上“必须定义至少一个排序操作: =" - 通过错误消息
【解决方案2】:

这是 Python 3 中一项重大而有意的更改。有关详细信息,请参阅 here

  • 当操作数没有有意义的自然排序时,排序比较运算符(&lt;&lt;=&gt;=&gt;)会引发 TypeError 异常。因此,1 &lt; ''0 &gt; Nonelen &lt;= len 等表达式不再有效,例如None &lt; None 引发 TypeError 而不是返回 False。一个推论是对异构列表进行排序不再有意义——所有元素必须相互可比。请注意,这不适用于 ==!= 运算符:不同类型的对象总是比较不相等。
  • builtin.sorted()list.sort() 不再接受提供比较功能的 cmp 参数。请改用 key 参数。注: keyreverse 参数现在是“仅限关键字”。
  • cmp() 函数应被视为已消失,不再支持 __cmp__() 特殊方法。使用__lt__() 进行排序,使用__eq__()__hash__(),并根据需要进行其他丰富的比较。 (如果您确实需要 cmp() 功能,您可以使用表达式 (a &gt; b) - (a &lt; b) 作为 cmp(a, b) 的等效项。)

【讨论】:

    【解决方案3】:

    Python3 中的六个丰富的比较运算符

    __lt__(self, other) 
    __le__(self, other) 
    __eq__(self, other) 
    __ne__(self, other) 
    __gt__(self, other) 
    __ge__(self, other) 
    

    必须单独提供。这可以使用functools.total_ordering 缩写。

    然而,这在大多数情况下变得相当不可读和不切实际。您仍然必须将类似的代码片段放入 2 个函数中 - 或使用进一步的辅助函数。

    所以我更喜欢使用下面显示的 mixin 类 PY3__cmp__。这重新建立了单一的__cmp__ 方法框架,该框架在大多数情况下都非常清晰和实用。仍然可以覆盖选定的丰富比较。

    你的例子会变成:

     class point(PY3__cmp__):
          ... 
          # unchanged code
    

    PY3__cmp__ 混合类:

    PY3 = sys.version_info[0] >= 3
    if PY3:
        def cmp(a, b):
            return (a > b) - (a < b)
        # mixin class for Python3 supporting __cmp__
        class PY3__cmp__:   
            def __eq__(self, other):
                return self.__cmp__(other) == 0
            def __ne__(self, other):
                return self.__cmp__(other) != 0
            def __gt__(self, other):
                return self.__cmp__(other) > 0
            def __lt__(self, other):
                return self.__cmp__(other) < 0
            def __ge__(self, other):
                return self.__cmp__(other) >= 0
            def __le__(self, other):
                return self.__cmp__(other) <= 0
    else:
        class PY3__cmp__:
            pass
    

    【讨论】:

    • 我喜欢你编写运算符的方式。但是,请解释这个说法 (a > b) - (a
    • @theBuzzyCoder bool 只是int 的子类,所以TrueFalse 基本上分别是1 和0。由于cmp 如果其第一个参数小于其第二个参数则返回负值,如果参数相等则返回零,否则返回正值,您可以看到False - False == 0True - False == 1False - True == -1 提供cmp 的正确返回值。
    猜你喜欢
    • 2012-01-06
    • 2017-09-19
    • 1970-01-01
    • 2020-12-08
    • 1970-01-01
    • 2011-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多