sorted(key=lambda: ...) 后面的语法

Posted

技术标签:

【中文标题】sorted(key=lambda: ...) 后面的语法【英文标题】:Syntax behind sorted(key=lambda: ...) 【发布时间】:2012-02-16 12:02:32 【问题描述】:

我不太明白sorted() 参数背后的语法:

key=lambda variable: variable[0]

lambda 不是任意的吗?为什么variable 在看起来像dict 的地方出现两次?

【问题讨论】:

【参考方案1】:

我认为这里的所有答案都很好地涵盖了 lambda 函数在 sorted() 上下文中的作用的核心,但是我仍然觉得缺乏导致直观理解的描述,所以这是我的两个美分。

为了完整起见,我先声明一下: sorted() 返回一个已排序元素的列表,如果我们想要以特定方式排序,或者我们想要对一个复杂的元素列表进行排序(例如嵌套列表或元组列表)我们可以调用 key 参数。

对我来说,对 key 参数的直观理解,为什么它必须是可调用的,以及使用 lambda 作为(匿名)可调用函数来实现这一点分为两部分。

    使用lamba 最终意味着您不必编写(定义)整个函数,就像sblom 提供的示例那样。 Lambda 函数被创建、使用并立即销毁——因此它们不会用更多只会使用一次的代码来混淆您的代码。据我了解,这是 lambda 函数的核心实用程序,它对这种角色的应用是广泛的。它的语法纯粹是一种约定,本质上是一般程序语法的本质。学习语法并完成它。

Lambda 语法如下:

lambda input_variable(s): tasty one liner

lambda 是一个 python 关键字。

例如

In [1]: f00 = lambda x: x/2

In [2]: f00(10)
Out[2]: 5.0

In [3]: (lambda x: x/2)(10)
Out[3]: 5.0

In [4]: (lambda x, y: x / y)(10, 2)
Out[4]: 5.0

In [5]: (lambda: 'amazing lambda')() # func with no args!
Out[5]: 'amazing lambda'
    key 参数背后的想法是,它应该接受一组指令,这些指令基本上将“sorted()”函数指向那些应该用于排序的列表元素。当它说key= 时,它的真正含义是:当我遍历列表时,一次一个元素(即for e in some_list),我将把当前元素传递给key 参数指定的函数,然后使用它来创建一个转换后的列表,它将通知我最终排序列表的顺序。

检查一下:

In [6]: mylist = [3, 6, 3, 2, 4, 8, 23]  # an example list
# sorted(mylist, key=HowToSort)  # what we will be doing

基础示例:

# mylist = [3, 6, 3, 2, 4, 8, 23]
In [7]: sorted(mylist)
Out[7]: [2, 3, 3, 4, 6, 8, 23]  
# all numbers are in ascending order (i.e.from low to high).

示例 1:

# mylist = [3, 6, 3, 2, 4, 8, 23]
In [8]: sorted(mylist, key=lambda x: x % 2 == 0)

# Quick Tip: The % operator returns the *remainder* of a division
# operation. So the key lambda function here is saying "return True 
# if x divided by 2 leaves a remainer of 0, else False". This is a 
# typical way to check if a number is even or odd.

Out[8]: [3, 3, 23, 6, 2, 4, 8]  
# Does this sorted result make intuitive sense to you?

请注意,我的 lambda 函数告诉 sorted 在排序之前检查每个元素 e 是偶数还是奇数。

等等!您可能(或应该)想知道两件事。

首先,为什么奇数出现在偶数之前?毕竟,键值似乎是在告诉sorted 函数通过在x % 2 == 0 中使用mod 运算符来确定偶数的优先级。

第二,为什么偶数还是乱序? 2 在 6 之前,对吧?

通过分析这个结果,我们将更深入地了解“key”参数的实际工作原理,尤其是与匿名 lambda 函数结合使用时。

首先,您会注意到虽然赔率出现在偶数之前,但偶数本身并没有排序。为什么是这样?? Lets read the docs:

关键函数 从 Python 2.4 开始,list.sort() 和 sorted() 都添加了一个关键参数来指定要调用的函数 进行比较之前的每个列表元素。

这里我们必须在字里行间做一点阅读,但这告诉我们 sort 函数只被调用一次,如果我们指定 key 参数,那么我们按照 key 函数指向的值排序我们去。

那么使用模数的示例返回什么?一个布尔值:True == 1False == 0。那么 sorted 是如何处理这个键的呢?它基本上将原始列表转换为 1 和 0 的序列。

[3, 6, 3, 2, 4, 8, 23] 变为 [0, 1, 0, 1, 1, 1, 0]

现在我们正在取得进展。对转换后的列表进行排序会得到什么?

[0, 0, 0, 1, 1, 1, 1]

好的,现在我们知道为什么赔率会先于赔率。但下一个问题是:为什么在我的最终列表中,6 仍然排在 2 之前?嗯,这很容易 - 这是因为排序只发生一次! 那些 1 仍然代表原始列表值,它们彼此相对于它们的原始位置。由于排序只发生一次,并且我们不调用任何类型的排序函数来将原始偶数从低到高排序,因此这些值彼此之间保持其原始顺序。

最后的问题是:当我打印出最终的排序列表时,我如何从概念上考虑我的布尔值的顺序如何转换回原始值?

Sorted() 是一种内置方法(有趣的事实),它使用一种称为Timsort 的混合排序算法,它结合了合并排序和插入排序的各个方面。我似乎很清楚,当您调用它时,有一种机制将这些值保存在内存中,并将它们与由 (...!) lambda 函数 确定的布尔标识(掩码)捆绑在一起。顺序由它们从 lambda 函数计算的布尔标识确定,但请记住,这些子列表(1 和 0)本身并不是按其原始值排序的。因此,最终列表虽然由 Odds 和 Evens 组织,但不按子列表排序(在这种情况下,偶数是无序的)。赔率排序的事实是因为它们在原始列表中已经巧合了。从这一切中得出的结论是,当 lambda 进行该转换时,子列表的原始顺序将被保留。

那么这一切与最初的问题有什么关系,更重要的是,我们应该如何使用其关键参数和 lambda 实现 sorted() 的直觉?

这个 lambda 函数可以被认为是一个指向我们需要排序的值的指针,无论它是一个将值映射到由 lambda 函数转换的布尔值的指针,还是它是嵌套列表中的特定元素, tuple, dict 等,再次由 lambda 函数确定。

让我们尝试预测当我运行以下代码时会发生什么。

In [9]: mylist = [(3, 5, 8), (6, 2, 8), (2, 9, 4), (6, 8, 5)]
In[10]: sorted(mylist, key=lambda x: x[1])

我的sorted 电话显然说:“请对这个列表进行排序”。关键参数通过说'对于mylist中的每个元素x,返回该元素的第二个索引,然后按排序顺序对原始列表mylist的所有元素进行排序由 lambda 函数计算的列表。由于我们有一个元组列表,我们可以使用 lambda 函数从该元组返回一个索引元素。

将用于排序的指针是:

[5, 2, 9, 8] # the second element of each tuple

排序这个指针列表返回:

[2, 5, 8, 9]

将此应用于mylist,我们得到:

Out[10]: [(6, 2, 8), (3, 5, 8), (6, 8, 5), (2, 9, 4)]
# Notice the sorted pointer list is the same as the second index of each tuple in this final list

运行该代码,您会发现这是顺序。尝试使用此键函数对整数列表进行排序,您会发现代码中断(为什么?因为您当然不能索引整数)。

这是一个冗长的解释,但我希望这有助于 sort 您对使用 lambda 函数的直觉 - 作为 sorted() 中的关键参数,以及更多。

【讨论】:

优秀而全面的解释。这个答案值得100分。但我想知道为什么喜欢这个答案的人少。 感谢您的深入解释。我read the docs 并整理了操作方法,我不清楚key 函数背后的想法。如果你想理解sorted 函数,他们lambda 语法只是进入理解的方式。 这是最好的解释。这对于帮助我理解它的实际工作方式非常有用——我有点掌握了 lambda 函数,但是在 sorted() 的上下文中使用它没有任何意义。这真的很有帮助,谢谢! 这是一个绝妙的答案。向您致敬,先生。 这是我最喜欢的关于堆栈溢出的答案之一。谢谢!【参考方案2】:

key 是一个函数,将调用该函数以在比较集合项之前对其进行转换。传递给key 的参数必须是可调用的。

lambda 的使用创建了一个匿名函数(可调用)。在sorted 的情况下,可调用对象只接受一个参数。 Python 的lambda 非常简单。它真的只能做和回报一件事。

lambda 的语法是单词lambda,后跟参数名称列表,然后是单个代码块。参数列表和代码块用冒号表示。这类似于 python 中的其他构造,例如whileforif 等。它们都是通常具有代码块的语句。 Lambda 只是带有代码块的语句的另一个实例。

我们可以比较使用 lambda 和 def 来创建一个函数。

adder_lambda = lambda parameter1,parameter2: parameter1+parameter2
def adder_regular(parameter1, parameter2): return parameter1+parameter2

lambda 只是为我们提供了一种无需指定名称即可执行此操作的方法。这使得它非常适合用作函数的参数。

variable 在这里使用了两次,因为在冒号的左侧是参数的名称,而在右侧则是在代码块中用于计算某些内容。

【讨论】:

注意(给 OP):应避免将 lambda 分配给名称(即以匿名函数以外的方式使用它)。如果您发现自己这样做了,您可能应该只使用def【参考方案3】:

lambda 是一个 Python 关键字,用于generate anonymous functions。

>>> (lambda x: x+2)(3)
5

【讨论】:

为什么每个都有括号? 括号在3附近,因为它被传递给一个函数。括号在 lambda 周围,因此表达式不会被解析为 lambda x: x+2(3),这是无效的,因为 2 不是函数。 我不确定我是否喜欢“匿名”函数这个术语。我的意思是,他们没有被命名,所以匿名是“技术上”准确的。我宁愿将它们称为“临时函数”。但是,我是一个书呆子。【参考方案4】:

: 左边的variable 是参数名称。右边variable的使用是对参数的使用。

意思几乎完全一样:

def some_method(variable):
  return variable[0]

【讨论】:

【参考方案5】:

另一个使用 sorted() 函数与 key=lambda 的示例。假设您有一个元组列表。在每个元组中,您都有汽车的品牌、型号和重量,并且您希望按品牌、型号或重量对这个元组列表进行排序。你可以用 lambda 来做。

cars = [('citroen', 'xsara', 1100), ('lincoln', 'navigator', 2000), ('bmw', 'x5', 1700)]

print(sorted(cars, key=lambda car: car[0]))
print(sorted(cars, key=lambda car: car[1]))
print(sorted(cars, key=lambda car: car[2]))

结果:

[('bmw', 'x5', '1700'), ('citroen', 'xsara', 1100), ('lincoln', 'navigator', 2000)]
[('lincoln', 'navigator', 2000), ('bmw', 'x5', '1700'), ('citroen', 'xsara', 1100)]
[('citroen', 'xsara', 1100), ('bmw', 'x5', 1700), ('lincoln', 'navigator', 2000)]

【讨论】:

【参考方案6】:

lambda 是一个匿名函数,而不是任意函数。被接受的参数将是您正在使用的变量,以及您对其进行排序的列。

【讨论】:

【参考方案7】:

简单且不费时的答案,并提供与所提问题相关的示例 按照这个例子:

 user = ["name": "Dough", "age": 55, 
            "name": "Ben", "age": 44, 
            "name": "Citrus", "age": 33,
            "name": "Abdullah", "age":22,
            ]
    print(sorted(user, key=lambda el: el["name"]))
    print(sorted(user, key= lambda y: y["age"]))

看看列表中的名字,它们以 D、B、C 和 A 开头。如果你注意年龄,它们分别是 55、44、33 和 22。 第一个打印代码

print(sorted(user, key=lambda el: el["name"]))

结果:

['name': 'Abdullah', 'age': 22, 
'name': 'Ben', 'age': 44, 
'name': 'Citrus', 'age': 33, 
'name': 'Dough', 'age': 55]

对名称进行排序,因为我们通过 key=lambda el: el["name"] 对名称进行排序,并且名称按字母顺序返回。

第二个打印代码

print(sorted(user, key= lambda y: y["age"]))

结果:

['name': 'Abdullah', 'age': 22,
 'name': 'Citrus', 'age': 33,
 'name': 'Ben', 'age': 44, 
 'name': 'Dough', 'age': 55]

按年龄排序,因此列表按年龄升序返回。

试试这个代码以获得更好的理解。

【讨论】:

【参考方案8】:

lambdasorted 的另一种用法如下:

给定输入数组:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]

该行:people_sort = sorted(people, key = lambda x: (-x[0], x[1])) 将给出一个people_sort 列表为[[7,0],[7,1],[6,1],[5,0],[5,2],[4,4]]

在这种情况下,key=lambda x: (-x[0], x[1]) 基本上告诉sorted 首先根据每个实例的第一个元素的值对数组进行排序(在降序按减号建议的顺序),然​​后在同一个子组中,根据每个实例的第二个元素进行排序(按升序排列,因为它是默认选项)。

希望这对您有一些有用的信息!

【讨论】:

【参考方案9】:

由于在 sorted() 的上下文中询问了 lambda 的用法,因此也请看一下 https://wiki.python.org/moin/HowTo/Sorting/#Key_Functions

【讨论】:

【参考方案10】:

只是换个说法,排序函数中的键(可选。执行以确定顺序的函数。默认为无)需要一个函数,并且您使用 lambda。

要定义 lambda,您指定要排序的对象属性,python 内置的 sorted 函数会自动处理它。

如果要按多个属性排序,则指定 key = lambda x: (property1, property2)。

要指定 order-by,请将 reverse= true 作为 sorted 函数的第三个参数(可选。布尔值。False 将升序排序,True 将降序排序。默认为 False)。

【讨论】:

以上是关于sorted(key=lambda: ...) 后面的语法的主要内容,如果未能解决你的问题,请参考以下文章

Python sorted函数|sorted([13,1,237,89,100],key=lambda x:len(str(x)))

python3排序 sorted(key=lambda)

python sorted 和 lambda结合排序 例子

sorted() 和 lambda --> 应用:字典排序

python里 key=lambda d:d[0]是啥意思?谢谢

lambda函数/排序/filter/map