我读到了包装器函数,它将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
答案 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__
没有。多年来,这让很多人感到困惑,同时删除了cmp
和cmp
参数来排序函数,从而消除了这种混淆。
与此同时,如果您阅读排序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]