按键的一部分进行倒序排序。

3

我需要对一个对象列表进行排序。目前,我是这样做的:

mylist = [aobject, bobject, cobject]
mylist.sort(key=mykey)
def mykey(sortelem):
    attribute1 = sortelem.attribute1
    attribute2 = sortelem.attribute2
    return (attribute1, attribute2)

现在我想按 attribute1 升序排序,但是 attribute2 降序。我可以像这样做:
return (attribute1, -1 * attribute2)

但这看起来很不符合Python的风格。我在网上搜索过,但这是一个非常难以搜索的事情。


4
这就是你该做的。 - jonrsharpe
1个回答

3
TL;DR:您可以对需要的位置添加 reversed=True 参数进行两次排序。先按次要排序,这样做的原因是 Python 的排序是稳定的。
如果attribute2是一个数字,那么在元组中使用- attribute2代替attribute2是最简单的方法。
如果attribute2是一个字符串或其他不能以有意义的方式乘以-1的东西,则此技巧无法使用。
然而,请注意,Python 中保证 lst.sortsorted稳定排序算法。特别地,这意味着以下两个代码片段是等价的:
# SORT ACCORDING TO A TUPLE
l.sort(key=lambda x: (x[0], x[1]))

# SORT TWICE
l.sort(key=lambda x: x[1])
l.sort(key=lambda x: x[0])

请注意第二个版本中的排序方式,我们首先根据tie-breaker排序,然后再根据主要标准排序。
对于第一个版本,如果该标准是数值型的,我们可以通过将其乘以-1来按照两个标准中的一个递减顺序进行排序。
l.sort(key=lambda x: (x[0], -x[1]))

然而,使用第二个版本,我们可以按照其中一个标准的降序排序,使用可选参数 reverse=True,这适用于数字以及其他类型,例如字符串。

l.sort(key=lambda x: x[1], reverse=True)
l.sort(key=lambda x: x[0])

最后,需要注意的是,您可以使用operator.itemgetteroperator.attrgetter来作为排序键,而不是使用deflambda定义自定义函数。

例如,以下两个代码片段是等价的:

# FIRST VERSION
l.sort(key=lambda x: (x[0], x[1]))

# SECOND VERSION
from operator import itemgetter
l.sort(key=itemgetter(0, 1))

以下三个代码片段等效:
# FIRST VERSION
def mykey(x):
    x1 = x.attribute1
    x2 = x.attribute2
    return (x1, x2)
l.sort(key=mykey)

# SECOND VERSION
l.sort(key=lambda x: (x.attribute1, x.attribute2))

# THIRD VERSION
from operator import attrgetter
l.sort(key=attrgetter('attribute1', 'attribute2'))

1
简而言之:您可以排序两次,添加一个reversed=True参数。首先按照优先级较低的进行排序。这是因为Python中的排序是稳定的。感谢Stef的回答,我的评论是从中得出的(: - mteam88
@mteam88 我在答案顶部添加了你的 TL;DR。 - Stef
如何对类似于 [("bcdes", 123), ("dafds", 32432), ("dsnt", 2305)] 的列表进行排序,其中无法在一个属性上应用“-1”?是否有一种一行代码的解决方案,而不是两次排序? - noobie2023
@JeffXi 如果你想要反转所有属性的顺序,那么你可以使用reverse=True进行一次排序。如果你只想反转某些属性的顺序而不是全部,那么我不知道比乘以-1或者排序两次更简单的解决方案了。理论上你可以想出一些转换方式来反转字符串比较的顺序,但这听起来比排序两次还要复杂。 - Stef
@JeffXi 或者你可以定义一个包装类 Inversed,它可以反转比较运算符并将其用作转换:`>>> from functools import total_ordering
@total_ordering ... class Inversed: ... def init(self, x): ... self.x = x ... def eq(self, other): ... return (self.x == other.x) ... def lt(self, other): ... return (other.x <= self.x)然后l = [("bcdes", 123), ("dafds", 32432), ("dafds", 3400), ("dsnt", 2305)]; r = sorted(l, key=lambda p: (Inversed(p[0]), p[1])); print(r)`
- Stef

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