Python的cmp_to_key函数如何工作?

mat*_*ots 34 python sorting algorithm cmp python-internals

我在这里遇到了这个功能.

我对这将如何实现感到困惑 - 如何key通过cmp_to_key知道给定元素的"位置"而不检查给定元素与其他感兴趣元素的比较来生成函数?

Mar*_*ers 46

cmp_to_key方法返回一个充当代理键的特殊对象:

class K(object):
    __slots__ = ['obj']
    def __init__(self, obj, *args):
        self.obj = obj
    def __lt__(self, other):
        return mycmp(self.obj, other.obj) < 0
    def __gt__(self, other):
        return mycmp(self.obj, other.obj) > 0
    def __eq__(self, other):
        return mycmp(self.obj, other.obj) == 0
    def __le__(self, other):
        return mycmp(self.obj, other.obj) <= 0
    def __ge__(self, other):
        return mycmp(self.obj, other.obj) >= 0
    def __ne__(self, other):
        return mycmp(self.obj, other.obj) != 0
    def __hash__(self):
        raise TypeError('hash not implemented')
Run Code Online (Sandbox Code Playgroud)

排序时,每个键将与序列中的大多数其他键进行比较.位于0的元素是否低于或大于其他对象?

每当发生这种情况时,都会调用特殊方法钩子,因此__lt__或被__gt__调用,代理键会转而调用该cmp方法.

所以列表[1, 2, 3]被排序为[K(1), K(2), K(3)],如果K(1)比较,K(2)看看是否K(1)更低,然后K(1).__lt__(K(2))被调用,这被转换为mycmp(1, 2) < 0.

这是怎样的老cmp方法,工作无论如何 ; 返回-1,0或1,具体取决于第一个参数是否小于,等于或大于第二个参数.代理键将这些数字转换回比较运算符的布尔值.

代理键在任何时候都不需要知道关于绝对位置的任何信息.它只需要知道一个它正在与其他比较对象和特殊方法挂钩提供了其他对象.

  • @jeremyjjbrown:`cmp_to_key`函数仅用于支持遗留的Python 2排序代码库.更简单的pythonic方法是使用`sorted`函数替换`sorted()`,如果排序算法调用`cmp()`类方法,直接实现自定义丰富的比较挂钩. (3认同)
  • 有时很难想出一个`key`但很容易想出一个比较函数...例如,我想对一个数字列表进行排序,以便所有奇数首先按升序排列,然后全部均匀接下来是数字,按降序排列. (2认同)
  • @rlbond 返回一个元组,其中键为“(n % 2 == 0, (n % 2 and 1 or -1) * n)”。 (2认同)
  • 我知道你可以解决这个问题.但我可以很容易地提出一些更困难的事情. (2认同)
  • @rlbond:然后创建定义自己排序的对象;您可以将它们用作密钥。这包括定义你自己的丰富的比较钩子,就像 `cmp_to_key()` 使用的 `K` 类所做的那样。 (2认同)
  • @user49117:每个元素只计算一次键,之后键的丰富比较钩子的使用频率将与 Python 2 中的“cmp”函数的使用频率完全相同。是的,如果可以的话,最好通过指定直接排序键函数来使用 [Swartzian 变换](https://en.wikipedia.org/wiki/Schwartzian_transform),但它并不比使用Python 2 中的“cmp”回调应该是这样。 (2认同)