【问题标题】:Custom Python list sorting自定义 Python 列表排序
【发布时间】:2012-08-04 17:32:55
【问题描述】:

我正在重构我的一些旧代码并遇到了这个问题:

alist.sort(cmp_items)

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

代码有效(我大约在 3 年前写了它!)但是我在 Python 文档的任何地方都找不到这个东西,每个人都使用 sorted() 来实现自定义排序。有人能解释一下为什么会这样吗?

【问题讨论】:

  • sorted()sort() 以几乎相同的方式提供自定义排序,以调用约定的差异为模。
  • 确实,使用key 参数优于传递cmp 函数。 (后者甚至没有在 Python 3 中实现)
  • 这有点模棱两可,取决于列表中的项目是什么;您的代码要求它们具有属性foo,否则它会爆炸。最好为你的类定义一个自定义的__lt__() 方法,然后sorted()list.sort() 可以开箱即用。 (顺便说一句,对象不再需要定义 __cmp__(),只需 __lt__()See this

标签: python list sorting


【解决方案1】:

已记录在 here

sort() 方法采用可选参数来控制 比较。

cmp 指定两个参数的自定义比较函数(列表 items) 应该返回一个负数、零或正数 取决于第一个参数是否被认为小于, 等于或大于第二个参数: cmp=lambda x,y: cmp(x.lower(),y.lower())。默认值为无。

【讨论】:

  • 谢谢miles82 我在这里查看,在方法签名docs.python.org/tutorial/datastructures.html中看不到它
  • 我在您链接到的页面上看不到相同的文字。文档是否更改。此外,当我尝试使用cmp 时,我得到TypeError: 'cmp' is an invalid keyword argument for this function。这是怎么回事?
  • @HelloGoodbye sort() 在 Python 3 中没有 cmp 参数。当文档链接适用于 Python 2 时,这是一个旧答案。您可以找到旧文档 here 或阅读更多关于它here。如果您使用的是 Python 3,请改用 key argument
  • 如果你真的想提供比较功能呢?我想将字符串中的数字(任意长度,贪婪地挑选出来)视为符号,相当于如何处理单个字符。如果我可以提供比较功能,我知道如何轻松实现这一点,但如果我必须提供关键功能,我就不知道了。为什么要更改?
  • 我想如果字符串中包含的每个数字都使用按字典顺序排列数字的编码进行编码,例如Levenshtein coding,它仍然可以实现。但我认为这更像是一种解决方法,即sort 在 Python 3 中没有将比较函数作为参数,而不是我实际上想做的事情。
【解决方案2】:

附带说明,这里是实现相同排序的更好选择:

alist.sort(key=lambda x: x.foo)

或者:

import operator
alist.sort(key=operator.attrgetter('foo'))

查看Sorting How To,它非常有用。

【讨论】:

  • 关于算子的TIL,很有用。
【解决方案3】:

就像这个例子。你想对这个列表进行排序。

[('c', 2), ('b', 2), ('a', 3)]

输出:

[('a', 3), ('b', 2), ('c', 2)]

你应该按第二个项目对元组进行排序,然后是第一个:

def letter_cmp(a, b):
    if a[1] > b[1]:
        return -1
    elif a[1] == b[1]:
        if a[0] > b[0]:
            return 1
        else:
            return -1
    else:
        return 1

然后将其转换为按键函数:

from functools import cmp_to_key
letter_cmp_key = cmp_to_key(letter_cmp))

现在您可以使用自定义排序顺序了:

[('c', 2), ('b', 2), ('a', 3)].sort(key=letter_cmp_key)

【讨论】:

  • 它如何知道要排序的列表?
  • @CameronMonks yourList.sort(letter_cmp)
【解决方案4】:

这在 Python 3 中不起作用。

您可以使用 functools cmp_to_key 让旧式比较函数正常工作。

from functools import cmp_to_key

def cmp_items(a, b):
    if a.foo > b.foo:
        return 1
    elif a.foo == b.foo:
        return 0
    else:
        return -1

cmp_items_py3 = cmp_to_key(cmp_items)

alist.sort(cmp_items_py3)

【讨论】:

  • cmp_to_key 在用作无参数装饰器时也可以工作(将@cmp_to_key 放在def 之前的行中以进行比较功能),因此您无需调用cmp_to_key并自己分配结果
【解决方案5】:

更好:

student_tuples = [
    ('john', 'A', 15),
    ('jane', 'B', 12),
    ('dave', 'B', 10),
]

sorted(student_tuples, key=lambda student: student[2])   # sort by age
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]

取自:https://docs.python.org/3/howto/sorting.html

【讨论】:

    【解决方案6】:

    我知道很多人已经发布了一些很好的答案。不过,我想推荐一种无需导入任何库的简单易用的方法。

    l = [(2, 3), (3, 4), (2, 4)]
    l.sort(key = lambda x: (-x[0], -x[1]) )
    print(l)
    l.sort(key = lambda x: (x[0], -x[1]) )
    print(l)
    

    输出将是

    [(3, 4), (2, 4), (2, 3)]
    [(2, 4), (2, 3), (3, 4)]
    

    输出将根据我们以元组格式提供的参数的顺序进行排序

    【讨论】:

    • 对于排序,首先,它会检查元组中的第一项,并尝试根据你给出的符号进行排序('-'表示反向排序)。如果它无法使用第一项进行排序,那么它会考虑元组中的第二项......等等......
    猜你喜欢
    • 2015-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-06
    • 1970-01-01
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多