前言

在Python2 的list中提供了一个sort函数,这个sort函数可以自定义一个排序规则,只需要参数一个cmp参数就可以了,这个cmp参数就是两个对象做比较的依据,然而在Python3中的sort却取消了这个cmp参数,只保留了一个key对象,这个key只有一个参数,就是对要排序的对象进行处理,提取出一个数据,这个数据可以直接用来被比较。但是在Python3的functools模块中有提供了一个cmp_to_key函数,通过这个函数可以把一个cmp函数变成一个key函数,从而可以实现自定义排序规则。

自定义排序示例

现在定义整数的比较规则,对整数a和b进行组合,组成ab和ba的形式。如果ab>ba,说明a优先级比较高,a应该比较大。针对这个问题排序不能够直接提取出一个key,那么就只能使用cmp_to_key函数进行转换。

代码示例如下

def cmp(a,b):
str1 = str(a)+str(b)
str2 = str(b)+str(a)
if str1 > str2:
return 1
elif str1 < str2:
return -1
else:
return 0
if __name__ == '__main__':
nums = [1,3,20,15]
nums.sort(key=functools.cmp_to_key(cmp))
print(nums)

原理

上面一个cmp_to_key函数就把cmp函数变成了一个参数的key函数,那么这个函数背后究竟做了什么,看下源码就知道了.源代码如下

def cmp_to_key(mycmp):
"""Convert a cmp= function into a key= function"""
class K(object):
__slots__ = ['obj']
def __init__(self, obj):
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
__hash__ = None
return K

这段代码很巧妙,在函数内部创建了一个class,并且返回了这个class,在这个class中调用了传入的cmp函数进行了运算符重载。这样使得两个class的对象就可以进行比较了。

梳理一下流程,在sort函数中,如果传入了key函数,它会在比较两个对象的时候,将两个对象作为参数传到key函数里面,然后对key函数的结果进行比较,从而完成了排序。当这里返回一个class的时候,这个key函数实际上就是class的构造函数,实际上就是将要比较的对象作为参数创建了两个K的对象,这两个对象进行过运算符重载,是可以比较的,比较的规则就是传入的cmp所定义的规则。这样就实现了自定义排序规则了。

总结

最开始不太能理解这段代码,主要是习惯了Java中严格的类型机制。class和method或者function都被自己严格区分开了。然而在python里一切皆对象,没有那么多严格的类型区分,大概这也是python的魅力所在吧。