选项1:
key=lambda d:(d['rank']==0, d['rank'])
选项 2:
key=lambda d:d['rank'] if d['rank']!=0 else float('inf')
演示:
“我想按排名值对其进行排序,排序如下:1-2-3-4-0-0-0。” ——原海报
>>> sorted([0,0,0,1,2,3,4], key=lambda x:(x==0, x))
[1, 2, 3, 4, 0, 0]
>>> sorted([0,0,0,1,2,3,4], key=lambda x:x if x!=0 else float('inf'))
[1, 2, 3, 4, 0, 0]
补充评论:
“请你向我(一个 Python 新手)解释它在做什么?我可以看到它是一个 lambda,我知道它是一个匿名函数:括号中的位是什么?” - OP评论
索引/切片符号:
itemgetter('rank')
和函数是一样lambda x: x['rank']
的:
def getRank(myDict):
return myDict['rank']
这[...]
被称为索引/切片表示法,请参阅解释 Python 的切片表示法- 另请注意,这someArray[n]
是许多编程语言中用于索引的常用表示法,但可能不支持形式的切片[start:end]
或[start:end:step]
.
key=
vs cmp=
vs 丰富的比较:
至于发生了什么,有两种常见的方法来指定排序算法的工作方式:一种是使用key
函数,另一种是使用cmp
函数(现在在 python 中已弃用,但用途更广泛)。虽然cmp
函数允许您任意指定两个元素应如何比较(输入:a
、b
;输出:a<b
或a>b
或a==b
)。虽然合法,但它并没有给我们带来任何重大好处(我们不得不以一种尴尬的方式复制代码),并且关键功能更适合您的情况。cmp=
(有关如何以优雅但可能过度的方式隐式定义,请参阅“对象丰富的比较” 。)
实现您的关键功能:
不幸的是,0 是整数的一个元素,因此具有自然排序:0 通常 < 1,2,3... 因此,如果我们想强加一个额外的规则,我们需要在“更高级别”对列表进行排序。我们通过将键设为元组来做到这一点:元组首先按其第一个元素排序,然后按其第二个元素排序。True 总是排在 False 之后,所以所有 True 都排在 False 之后;然后它们将正常排序:(True,1)<(True,2)<(True,3)<...
, (False,1)<(False,2)<...
, (False,*)<(True,*)
. 替代方案(选项 2)仅为 rank-0 字典分配无穷大的值,因为它保证高于任何可能的等级。
更一般的选择- 对象丰富的比较:
更通用的解决方案是创建一个表示记录的类,然后实现__lt__
, __gt__
, __eq__
, __ne__
, __gt__
,__ge__
和所有其他丰富的比较运算符,或者只实现其中一个__eq__
并使用@functools.total_ordering
装饰器。这将导致该类的对象在您使用比较运算符(例如x=Record(name='Joe', rank=12)
y=Record(...)
x<y
)时使用自定义逻辑;由于sorted(...)
函数<
在比较排序中默认使用和其他比较运算符,这将使排序时的行为自动进行,在您使用<
和其他比较运算符的其他情况下。这可能会也可能不会过多,具体取决于您的用例。
更清洁的替代方案- 不要用语义重载 0:
但是我应该指出,将 0 放在 1、2、3、4 等后面有点人为。这是否合理取决于rank=0是否真的意味着rank=0;如果 rank=0 真的“低于” rank=1 (反过来又真的“低于” rank=2 ...)。如果情况确实如此,那么您的方法非常好。如果不是这种情况,那么您可能会考虑省略'rank':...
条目而不是设置'rank':0
。然后你可以使用 Lev Levitsky 的答案排序'rank' in d
,或者:
具有不同方案的选项1:
key=lambda d: (not 'rank' in d, d['rank'])
具有不同方案的选项2:
key=lambda d: d.get('rank', float('inf'))
旁注:依靠 python 中无穷大的存在几乎是一种 hack,使任何提到的解决方案(元组、对象比较)、Lev 的filter-then-concatenate 解决方案,甚至可能稍微复杂的cmp
解决方案(键入wilson),更普遍适用于其他语言。