如何在Python 3中使用自定义比较函数?

131

Python 2.x中,我可以将自定义函数传递给sorted和.sort函数。

>>> x=['kar','htar','har','ar']
>>>
>>> sorted(x)
['ar', 'har', 'htar', 'kar']
>>> 
>>> sorted(x,cmp=customsort)
['kar', 'htar', 'har', 'ar']

因为在我的语言中,辅音按照这个顺序出现

"k","kh",....,"ht",..."h",...,"a"

但在Python 3.x中,似乎我无法传递cmp关键字参数

>>> sorted(x,cmp=customsort)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'cmp' is an invalid keyword argument for this function

有没有替代方案,还是我需要自己编写排序函数?

注意:我简化了使用“k”,“kh”等。实际字符是Unicode甚至更加复杂,有时元音在辅音之前和之后,我已经编写了自定义比较函数,这部分没问题。唯一的问题是我无法将自定义比较函数传递给sorted或.sort。


你尝试过只使用 sorted(x) 吗? - SilentGhost
@SilentGhost,为了确认,我刚刚又尝试了一下,当然没有成功,因为“我”的原始语言不在操作系统支持的区域设置列表中进行排序。 - YOU
1
你可以把cmp函数封装成一个关键字函数。在HowToSorting网站上搜索cmp_to_key。 - Frank
1
这是一个类似的内容 https://dev59.com/k6nka4cB1Zd3GeqPU96x#49327441 - Eziz Durdyyev
6个回答

77

使用 key 关键字和 functools.cmp_to_key 来转换您的比较函数:

sorted(x, key=functools.cmp_to_key(customsort))

63

+1,看起来这个方法可以为我提供一种解决方案,但是我认为通过传递所有比较操作符“< > = ”给中间人,我可能会失去一些性能,因为我的原始自定义排序是用 C 编写的,其速度大约只有默认排序的一半。 - YOU
2
(刚看了一下你的个人资料)你们公司居然封锁了Google和StackOverflow的访问?他们能愚蠢到这种程度吗?不过关于你的回答:我对实际性能下降很感兴趣。你可以用timeit来测试一下吗? - Tim Pietzcker
4
我做了一些基准测试,看起来用自定义的 C 比较函数直接传递比直接使用大约慢 4 倍。 - YOU
2
如果我需要同时使用关键函数和cmp函数怎么办?我想按每个字典中的自定义键对字典列表进行排序。在Python 3.2中,sorted_rows = sorted(rows, key=itemgetter('name'), cmp=locale.strxfrm)会出现TypeError: 'cmp'是此函数的无效关键字参数:( - Alex Bitek
4
标准库中的 functools 模块提供了 cmp_to_key 函数:https://docs.python.org/3.6/library/functools.html。 - Martín Fixman
显示剩余2条评论

25

一个完整的 python3 cmp_to_key lambda 示例:

from functools import cmp_to_key

nums = [28, 50, 17, 12, 121]
nums.sort(key=cmp_to_key(lambda x, y: 1 if str(x)+str(y) < str(y)+str(x) else -1))

相较于常见的对象排序:

class NumStr:
    def __init__(self, v):
        self.v = v
    def __lt__(self, other):
        return self.v + other.v < other.v + self.v


A = [NumStr("12"), NumStr("121")]
A.sort()
print(A[0].v, A[1].v)

A = [obj.v for obj in A]
print(A)

19

你需要一种将每个单词转换成Python已知如何排序的内容的函数,而不是自定义sort()。例如,你可以将每个单词转换为数字列表,其中每个数字表示字母在你的字母表中出现的位置。类似这样:

my_alphabet = ['a', 'b', 'c']

def custom_key(word):
   numbers = []
   for letter in word:
      numbers.append(my_alphabet.index(letter))
   return numbers

x=['cbaba', 'ababa', 'bbaa']
x.sort(key=custom_key)

由于您的语言包括多字符字母,因此您的custom_key函数显然需要更复杂。不过,这应该可以给您一个大致的想法。


谢谢+1,这就是我认为的ICU方式。但是由于我的语言没有单词分隔符并且没有标准的罗马化规则,所以研究起来需要时间。 - YOU
对于一些不想使用cmp_to_key的特定示例,请参见例如 https://dev59.com/nHE85IYBdhLWcg3w8IbK、https://dev59.com/jJffa4cB1Zd3GeqP_Kyf、https://stackoverflow.com/questions/68968534。 - Karl Knechtel

4

我不知道这是否有帮助,但您可以查看 locale 模块。它似乎可以将区域设置为您的语言,并使用 locale.strcoll 使用您语言的排序规则来比较字符串。


这在流行的编程语言中确实是正确的,但我的语言并没有得到操作系统、ICU和unicode.org的完全支持,所以这是不可能的,但好建议加1。 - YOU

-3
请使用key参数。它接受一个函数,该函数接受正在处理的值并返回一个单个值,以指定排序时要使用的键。
sorted(x, key=somekeyfunc)

4
key参数只接受一个参数的函数,而cmp参数需要两个参数,它们的行为是不同的。我刚刚进行了测试,出现了错误,因为key关键字只传递一个参数,导致“TypeError: customsort() takes exactly 2 positional arguments (1 given)”(TypeError:customsort()接受恰好2个位置参数(已给定1个))。 - YOU

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