首先,如果您还没有阅读Sorting HOWTO,请务必阅读;一开始它可能解释不了很多.
对于第一个示例,两个IPv4地址,答案非常简单.
要比较两个地址,一件显而易见的事情是将它们都从点分四个字符串转换为4个整数的元组,然后只比较元组:
def cmp_ip(ip1, ip2):
ip1 = map(int, ip1.split('.'))
ip2 = map(int, ip2.split('.'))
return cmp(ip1, ip2)
更好的事情是将它们转换为表示IP地址并具有比较运算符的某种对象.在3.4中,stdlib内置了这样的对象.让我们假设2.7也是如此:
def cmp_ip(ip1, ip2):
return cmp(ipaddress.ip_address(ip1), ipaddress.ip_address(ip2))
很明显,它们都更容易作为关键功能:
def key_ip(ip):
return map(int, ip.split('.'))
def key_ip(ip):
return ipaddress.ip_address(ip)
对于第二个示例,火腿无线电呼号:为了编写cmp函数,您必须能够将每个火腿地址分解为字母,数字,字母部分,然后比较数字,然后比较第一个字母,然后比较第二个字母.为了编写键功能,您必须能够将火腿地址分解为字母,数字,字母部分,然后返回一个元组(数字,第一个字母,第二个字母).同样,按键功能实际上更容易,而不是更难.
的确,对于大多数人都能想到的例子,情况就是如此.最终,最复杂的比较最终归结为将复杂的转换成部分序列,然后对该序列进行简单的字典比较.
这就是为什么在2.4版本中不推荐使用cmp函数,而在3.0版本中最终将其删除的原因.
当然,在某些情况下,cmp函数更易于阅读-人们尝试提出的大多数示例都是错误的,但也有一些示例.而且还有已经使用了20年的代码,没有人愿意以新的术语重新思考它而没有任何好处.对于这些情况,您的得分为cmp_to_key.
实际上,不推荐使用cmp的另一个原因是在此原因之上,也许还有三分之一.
在Python 2.3中,类型具有__cmp__方法,该方法用于处理所有运算符.在2.4中,他们增加了__lt __,__ eq__等六种方法作为替代.这样可以提供更大的灵活性-例如,您可以选择非总排序的类型.因此,2.3与< b,实际上是在做a .__ cmp __(b)< 0,以非常明显的方式映射到cmp参数.但在2.4中,< b做a .__ lt __(b),不是.多年来,这使很多人感到困惑,同时删除__cmp__和cmp参数以进行排序功能也消除了这种混乱.
同时,如果您阅读了Sorting HOWTO,您会注意到在进行cmp之前,做这种事情的唯一方法是decorate-sort-unecorate(DSU).请注意,如何将良好的键函数映射到良好的DSU排序(反之亦然)是一目了然的,但是使用cmp函数绝对不是显而易见的.我不记得有人在py3k列表上明确提到过这个,但是我怀疑人们在决定是否最终杀死cmp时可能已经想到了这一点.