Python:functools cmp_to_key函数如何工作?

mat*_*sco 28 python sorting deprecated

在Python中,list.sort方法和sorted内置函数都接受一个名为的可选参数key,该参数是一个函数,给定列表中的元素返回其排序键.

较旧的Python版本使用了一个不同的方法,cmp而不是使用参数,这是一个函数,给定列表中的两个元素,如果第一个小于第二个,则返回负数,如果有等于则返回零,如果第一个是,则返回正数更大.在某些时候,此参数已弃用,并未包含在Python 3中.

有一天,我想以一种cmp函数比一个函数更容易编写的方式对元素列表进行排序key.我并不想这样我阅读文档,我发现有一个名为funtion使用弃用的功能cmp_to_keyfunctools,正如他的名字状态,接收模块cmp功能,并返回key一个......或者这就是我想直到我阅读了文档中包含的这个高级函数的源代码(或至少是等效版本)

def cmp_to_key(mycmp):
    'Convert a cmp= function into a key= function'
    class K(object):
        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
    return K
Run Code Online (Sandbox Code Playgroud)

尽管事实上cmp_to_key按预期工作,但我感到惊讶的是,这个函数不会返回函数而是返回K类.为什么?它是如何工作的?我猜这个sorted函数在内部检查cmp是函数还是K类或类似东西,但我不确定.

PS:尽管他很奇怪,但我发现K级非常有用.检查此代码:

from functools import cmp_to_key

def my_cmp(a, b):
    # some sorting comparison which is hard to express using a key function

class MyClass(cmp_to_key(my_cmp)):
    ...
Run Code Online (Sandbox Code Playgroud)

这样,默认情况下,MyClass的任何实例列表都可以按照中定义的条件进行排序 my_cmp

Ana*_*mar 26

不,sorted函数(或list.sort)内部不需要检查它收到的对象是函数还是类.它所关心的只是它在key参数中接收的对象应该是可调用的,并且应该返回一个值,该值可以在调用时与其他值进行比较.

类也可以调用,当你调用一个类时,你会收到该类的实例.

要回答你的问题,首先我们需要理解(至少在基本层面)key参数是如何工作的 -

  1. key调用被调用用于每个元素,并将其接收回与它应该排序的对象.

  2. 在接收到新对象后,它将其其他对象进行比较(再次通过key使用其他元素调用callable来接收).

现在需要注意的重要一点是,将新object收到的内容与其他相同的对象进行比较.

现在进入等效代码,当您创建该类的实例时,可以使用您的mycmp函数将其与同一类的其他实例进行比较.排序时排序值会比较这些对象(实际上)调用您的mycmp()函数以确定该值是否小于或大于另一个对象.

打印语句示例 -

>>> def cmp_to_key(mycmp):
...     'Convert a cmp= function into a key= function'
...     class K(object):
...         def __init__(self, obj, *args):
...             print('obj created with ',obj)
...             self.obj = obj
...         def __lt__(self, other):
...             print('comparing less than ',self.obj)
...             return mycmp(self.obj, other.obj) < 0
...         def __gt__(self, other):
...             print('comparing greter than ',self.obj)
...             return mycmp(self.obj, other.obj) > 0
...         def __eq__(self, other):
...             print('comparing equal to ',self.obj)
...             return mycmp(self.obj, other.obj) == 0
...         def __le__(self, other):
...             print('comparing less than equal ',self.obj)
...             return mycmp(self.obj, other.obj) <= 0
...         def __ge__(self, other):
...             print('comparing greater than equal',self.obj)
...             return mycmp(self.obj, other.obj) >= 0
...         def __ne__(self, other):
...             print('comparing not equal ',self.obj)
...             return mycmp(self.obj, other.obj) != 0
...     return K
...
>>> def mycmp(a, b):
...     print("In Mycmp for", a, ' ', b)
...     if a < b:
...         return -1
...     elif a > b:
...         return 1
...     return 0
...
>>> print(sorted([3,4,2,5],key=cmp_to_key(mycmp)))
obj created with  3
obj created with  4
obj created with  2
obj created with  5
comparing less than  4
In Mycmp for 4   3
comparing less than  2
In Mycmp for 2   4
comparing less than  2
In Mycmp for 2   4
comparing less than  2
In Mycmp for 2   3
comparing less than  5
In Mycmp for 5   3
comparing less than  5
In Mycmp for 5   4
[2, 3, 4, 5]
Run Code Online (Sandbox Code Playgroud)

  • “为什么 2 与 4 比较两次”,以及“为什么 5 不与 2 比较?”。让我知道。我不知道,这太过分了。当“cmp_to_key(-1)”返回到“key”值时会发生什么? (3认同)

mat*_*sco 6

我刚刚意识到,尽管 K 类不是一个函数,但它是一个可调用的,因为它是一个类!类是可调用的,当被调用时,会创建一个新实例,通过调用相应的实例来初始化它__init__,然后返回该实例。

这样,它的行为就像一个key函数,因为 K 在调用时接收该对象,并将该对象包装在 K 实例中,该实例可以与其他 K 实例进行比较。

如我错了请纠正我。我觉得我正在进入我不熟悉的元类领域。