使用自定义的cmp函数对Python中的sorted函数进行排序

8

我希望使用不同的比较函数对字典中的项目进行排序。 请看下面的示例代码。最后一部分使用 cmpRatio 函数与 sorted() 一起使用时出现问题。我不确定自己做错了什么。感谢您提供任何想法!

mydict = { 'a1': (1,6),
          'a2': (10,2),
          'a3': (5,3),
          'a4': (1,2),
          'a5': (3,9),
          'a6': (9,7) }

# sort by first element of the value tuple: WORKS
print sorted(mydict.iteritems(), key=lambda (k,v): v[0])

# sort by second element of the value tuple: WORKS
print sorted(mydict.iteritems(), key=lambda (k,v): v[1])

# THIS is what I can't get working:
def cmpRatio(x,y):
   sx = float(x[0])/x[1]
   sy = float(y[0])/y[1]
   return sx < sy

# sort by sum of the elements in the value tuple: DOES NOT WORK
print sorted(mydict.iteritems(), key=lambda (k,v): v, cmp=cmpRatio)

请尝试使用编辑器上的小 { } 框来格式化您的代码。 - Cameron Sparr
3个回答

6

尽可能避免使用cmp函数,因为它们速度较慢。每次比较都需要重新评估。使用key可使键仅需计算一次。

print sorted(mydict.iteritems(), key=lambda (k,v): float(v[0])/v[1])

此外,你说你想按值项目的总和排序,但你却按差异排序。如果按总和排序,应该是这样的:
print sorted(mydict.iteritems(), key=lambda (k,v): sum(v))

如其他答案所述,如果您真的想定义一个cmp函数,那么您没有返回正确的值(必须是-1、0或1)。

return cmp(sx,sy)

如果你只是使用lambda表达式来获取值,你可以用itemgetter替换它。这比在Python端使用函数更快:

from operator import itemgetter

print sorted(mydict.iteritems(), key=itemgetter(1), cmp=cmpRatio)

如果您想存储排序操作,最好存储键函数:

key_ops = {
    'sum': lambda (k,v): sum(v),
    'ratio': lambda (k,v): float(v[0])/v[1]),
}

def print_op(aDict, opName):
    print sorted(aDict.iteritems(), key=key_ops[opName])

... # some place later in code
print_op(mydict, 'sum')

对于混淆我造成的困扰,求和(sum)和比率(ratio)是我想要使用的各种比较器函数中的两个。您的解决方案完全有效,但我的示例不仅速度慢,而且根本无法工作,我仍然想了解我做错了什么。我希望能够定义比较器函数并将它们用作某些其他优化函数的参数,这就是我尝试使用cmp的根本原因。谢谢! - Ruxandra Palmtag
@RuxandraPalmtag:我仍然不知道为什么你想要一个cmp函数。相反,你应该为键定义多个lambda操作。但是,我已经更新了关于你的cmp函数的内容。 - jdi
哦,我现在明白了:返回值,我之前读过,只是没有注意 :) 谢谢您的效率建议,我会尝试使用带有lambda的键,并查看它如何适用于我的代码! - Ruxandra Palmtag

4
你的比较函数应该在第一个参数小于/等于/大于第二个值时返回一个(负数/零/正数)值(与C++中给定给std::sort(...)的比较器不同)。
例如,而不是
return sx < sy

执行

return cmp(sx,sy)

2
如果您想按值元组的总和排序(根据您的评论),您可以使用以下方法:
print sorted(mydict.iteritems(), key=lambda v: sum(v[1]))

如果你想按比率排序(根据你的代码):
print sorted(mydict.iteritems(), key=lambda v: float(v[1][0])/v[1][1])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接