Python3样式排序 - 旧密钥机制中的旧cmp方法功能?

时间:2015-05-05 01:59:33

标签: python sorting

我读到了包装器函数,它将cmp样式比较转换为Python 3中的键样式比较,其中cmp功能已被删除。

我有一点时间围绕Python3直键样式sorted()函数的方式,至少据我所知,只有一个为键指定的项目,可以让你例如,正确地比较两个用于订购的IP。或火腿电话。

然而对于cmp,它没有任何东西:sorted()和sort()用两个ips调用你,你查看了相应的部分,做出了你的决定。

def ipCompare(dqA,dqB):
    ...

ipList = sorted(ipList,cmp=ipCompare)

与火腿无线电通话相同。排序不是字母;电话通常是字母+数字+字母;第一个排序优先级是数字部分,然后是第一个字母,然后是最后一个字母(s。)

使用cmp ......没有汗水。

def callCompare(callA,callB):
    ...

hamlist = sorted(hamlist,cmp=callCompare)

使用Python3 ...没有经过包装器的箍跳......并且传递了一个项目......我想......怎么能这样做?

如果绝对需要包装器......那么为什么要首先在Python3中删除cmp?

我确定我错过了什么。我无法看到它。 :/

好的,现在我知道我错过了什么。 IPs的解决方案在下面的答案中给出。这是我提出的用于排序公共前缀,区域,后缀形式的火腿调用的密钥:

import re

def callKey(txtCall):
    l = re.split('([0-9]*)',txtCall.upper(),1)
    return l[1],l[0],l[2]

hamList = ['N4EJI','W1AW','AA7AS','na1a']

sortedHamList = sorted(hamList,key=callKey)

sortedHamList结果为['na1a','W1AW','N4EJI','AA7AS']

详情:

  • AA7AS来自callKey() 7,AA,AS
  • N4EJI来自callKey() 4,N,EJI
  • W1AW来自callKey() 1,W,AW
  • na1a来自callKey() 1,NA,A

3 个答案:

答案 0 :(得分:5)

首先,如果您还没有阅读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功能,你必须能够将每个火腿地址分成字母,数字,字母部分,然后比较数字,然后比较第一个字母,然后比较第二个字母。为了编写key函数,您必须能够将火腿地址分解为字母,数字,字母部分,然后返回(数字,第一个字母,第二个字母)的元组。关键功能实际上更容易,而不是更难

实际上,对于大多数示例,任何人都可以提出这种情况。最复杂的比较最终归结为复杂的部分序列转换,然后对该序列进行简单的词典比较。

这就是为什么cmp函数在2.4中被弃用并最终在3.0中删除的原因。

当然,在某些情况下,cmp函数更容易阅读 - 人们试图提出的大多数示例都是错误的,但也有一些。而且还有代码已经工作了20年,没有人想以新的方式重新考虑它,没有任何好处。对于这些情况,你有cmp_to_key

实际上还有另一个原因cmp被弃用,除此之外,可能还有三分之一。

在Python 2.3中,类型具有__cmp__方法,该方法用于处理所有运算符。在2.4中,他们将六种方法__lt____eq__等作为替代方法。这样可以提供更大的灵活性 - 例如,您可以拥有非全部订购的类型。因此,当比较a < b时,它实际上是a.__cmp__(b) < 0,它以非常明显的方式映射到cmp参数。但在2.4+中,a < b执行a.__lt__(b),而__cmp__没有。多年来,这让很多人感到困惑,同时删除了cmpcmp参数来排序函数,从而消除了这种混淆。

与此同时,如果您阅读排序HOWTO,您会注意到在我们key之前,执行此类操作的唯一方法是decorate-sort-undecorate(DSU)。请注意,如何将良好的cmp函数映射到良好的DSU排序,反之亦然,这显然是明显的,但使用cmp函数肯定不明显。我不记得有人在py3k列表中明确提到过这个,但我怀疑在决定是否最终杀死{{1}}时,人们可能已经知道了。

答案 1 :(得分:4)

要使用the new key argument,只需将比较分解为另一个已实现良好排序比较的对象,例如元组或列表(例如整数序列)。这些类型的效果很好,因为它们是按序列顺序排列的。

def ip_as_components (ip):
    return map(int, ip.split('.'))

sorted_ips = sorted(ips, key=ip_as_components)

每个组件的排序与传统的compare-and-then-compare-by函数中的各个测试相同。

看看HAM的订购情况可能如下:

def ham_to_components (ham_code):
    # .. decompose components based on ordering of each
    return (prefix_letters, numbers, postfix_letters)

key方法(类似于&#34;#34顺序;在其他语言中找到)通常是一个更简单,更自然的构造来处理 - 假设原始类型不< / em>已经完好无损。这种方法的主要缺点是部分反转(例如asc然后desc)排序可能很棘手,但这可以通过返回嵌套元组等来解决。

  

在Py3.0中,完全删除了cmp参数(作为简化和统一语言的更大努力的一部分,消除了丰富的比较与__cmp__()魔术方法之间的冲突)。

如果绝对需要sorted使用自定义&#34; cmp&#34;,cmp_to_key可以被轻易使用。

sorted_ips = sorted(ips, key=functools.cmp_to_key(ip_compare))

答案 2 :(得分:1)

根据官方文件 - https://docs.python.org/3/howto/sorting.html#the-old-way-using-the-cmp-parameter

  

将代码从Python 2.x移植到3.x时,如果用户提供比较功能并且需要将其转换为关键功能,则会出现这种情况。以下包装器使这很容易做到:

def cmp_to_key(mycmp):
    'Convert a cmp= function into a key= function'
    class K:
        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
  

要转换为关键功能,只需包装旧的比较功能:

>>> def reverse_numeric(x, y):
...     return y - x

>>> sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric))
[5, 4, 3, 2, 1]