使用'key'和lambda表达式的Python max函数

260

我来自面向对象编程(OOP)的背景,现在正在学习Python。 我正在使用max函数,该函数使用lambda表达式返回类型为Player的实例,在players列表中具有最大的totalScore

def winner():
    w = max(players, key=lambda p: p.totalScore)

该函数正确返回具有最大totalScorePlayer类型的实例。

我对以下三件事感到困惑:

  1. max函数如何工作?它所取的参数是什么?我查看了文档但无法理解。
  2. max函数中关键字key的用途是什么?我知道它在sort函数的上下文中也被使用
  3. lambda表达式的含义是什么?如何阅读它们?它们如何工作?

这些都是非常初级的概念问题,但这将帮助我理解语言。如果您能给出示例来解释,那就更好了。 谢谢。


哪个Python版本? - charmlessCoin
5
你有查阅文档吗? - Inbar Rose
@charmlessCoin Python 2.7.5 - Vijay
4
我查看了max函数的文档,不是很理解。 - Vijay
22
@InbarRose 这个页面现在实际上是谷歌搜索python max lambda的顶部结果,也许对新用户更有帮助。 - Mark
9个回答

391

lambda是一个匿名函数,它相当于:

def func(p):
   return p.totalScore     

现在,max变为:
max(players, key=func)

作为复合语句,def 语句不能在需要表达式的地方使用,这就是有时候会使用 lambda 的原因。
请注意,lambda 相当于在 def 的返回语句中放置的内容。因此,在 lambda 中不能使用语句,只允许使用表达式。

max是什么?

max(a, b, c, ...[, key=func]) -> value

如果只有一个可迭代的参数,返回其中最大的元素。如果有两个或更多的参数,则返回最大的参数。

因此,它只是返回最大的对象。


如何使用key

在Python 2中,默认情况下,key基于对象类型的一组规则进行比较(例如,字符串始终大于整数)。

要修改比较前的对象,或者根据特定属性/索引进行比较,必须使用key参数。

示例1:

一个简单的例子,假设您有一个以字符串形式表示的数字列表,但是您想按其整数值比较这些项。

>>> lis = ['1', '100', '111', '2']

这里的max使用原始值进行比较(字符串按字典顺序比较,因此输出为'2'):
>>> max(lis)
'2'

要按照整数值比较项目,请使用带有简单lambda的key:
>>> max(lis, key=lambda x:int(x))  # compare `int` version of each item
'111'

例子2:将max应用于元组列表。

>>> lis = [(1,'a'), (3,'c'), (4,'e'), (-1,'z')]

默认情况下,max 会按照第一个索引比较项目。如果第一个索引相同,则会比较第二个索引。就像我的例子一样,所有项目都有一个独特的第一个索引,所以你会得到这个答案:
>>> max(lis)
(4, 'e')

但是,如果你想按照索引1的值比较每个项目怎么办?简单:使用lambda

>>> max(lis, key = lambda x: x[1])
(-1, 'z')

比较包含不同类型对象的可迭代项:

混合项列表:

lis = ['1','100','111','2', 2, 2.57]

在Python 2中,可以比较两种不同类型的项目

>>> max(lis)  # works in Python 2
'2'
>>> max(lis, key=lambda x: int(x))  # compare integer version of each item
'111'

但在Python 3中你不能再这样做了

>>> lis = ['1', '100', '111', '2', 2, 2.57]
>>> max(lis)
Traceback (most recent call last):
  File "<ipython-input-2-0ce0a02693e4>", line 1, in <module>
    max(lis)
TypeError: unorderable types: int() > str()

但是这样有效,因为我们正在比较每个对象的整数版本:

>>> max(lis, key=lambda x: int(x))  # or simply `max(lis, key=int)`
'111'

1
我知道这可能有点老,但是我有一个关于这个的问题。我看到对于lambda函数,变量x或i或其他任何变量总是表示列表中该索引处的值。这个迭代是由max函数还是由lambda完成的?lambda函数是否总是遍历所有可能的值?例如:lengths = map(lambda word: len(word), words)其中words=['It', 'is', 'raining', 'cats', 'and', 'dogs']我看到lambda正在遍历列表中的每个单词。它总是这样做吗? - Mo2
2
在迭代中,使用的是 max 而不是 lambda(参数 key 是可选的),每个项都会传递给 key 参数指定的函数,函数返回值然后用于比较。 - Ashwini Chaudhary
4
以下是翻译的结果:针对那些通过谷歌搜索“max key parameter”而来的读者,max(lis, key=lambda x:int(x)) 可以简化为 max(lis, key=int)。在 Python 中,有一个内置函数 int()。同样地,你可以使用任何其他内置函数作为“key”参数。例如,你可以通过 max(lis, key=len) 来获取字符串列表 lis=['a', 'aa', 'aaa'] 的最长字符串。 - YOUNG
2
@YOUNG 我们可以使用任何函数作为关键参数,而不仅仅是内置函数。唯一的条件是该函数应该正确地接受由 maxminsorted 等传递给它的项目。此外,我在最后提到了 max(lis, key=int)。 :-) - Ashwini Chaudhary
@Ashwini Chaudhary.. 假设我有一个列表,如[1,2,3,4,5]。这里所有的项都是不同的。我正在使用给定的函数max(set(mylist),key=mylist.count)来查找最频繁的项。由于在这种情况下没有重复的元素,它返回最小的项。我们能否做些什么,使其在这种情况下返回零或空值? - vikrant rana

24

max函数的极度简化版本:

def max(items, key=lambda x: x):
    current = item[0]
    for item in items:
        if key(item) > key(current):
            current = item
    return current

关于lambda:

>>> ident = lambda x: x
>>> ident(3)
3
>>> ident(5)
5

>>> times_two = lambda x: 2*x
>>> times_two(2)
4

1
如果在lambda函数中使用input()函数,那么这个版本将是错误的。 - Shreyansh
所以,“key”不是字典中的键,而是“在比较之前影响值的计算函数”,因此它是一个输入函数... - Fenix Lam
@Shreyansh,我这里的函数只是为了直观地展示max在高层次上的作用,它不会像stdlib那样完全按照相同的模式调用key。如果你的函数具有副作用或者是不确定性的,那么可能会导致结果不一致。 - Markus Unterwaditzer

13

max函数用于从一个可迭代对象中获取最大值。

这些可迭代对象可以是列表、元组、字典等,甚至可以是自定义对象,就像您提供的示例一样。

max(iterable[, key=func]) -> value
max(a, b, c, ...[, key=func]) -> value

With a single iterable argument, return its largest item.
With two or more arguments, return the largest argument.

所以,key=func 基本上允许我们向函数传递一个可选的参数 key,该参数用于排序给定迭代器/参数并返回最大值。

lambda 是 Python 的关键字,充当伪函数的作用。因此,当您将 player 对象传递给它时,它将返回 player.totalScore。因此,传递给函数 max 的可迭代对象将根据给定给它的 player 对象的 totalScore 总分数 进行排序,并返回具有最大 totalScore 总分数player

如果未提供 key 参数,则按照默认 Python 排序返回最大值。

示例 -

max(1, 3, 5, 7)
>>>7
max([1, 3, 5, 7])
>>>7

people = [('Barack', 'Obama'), ('Oprah', 'Winfrey'), ('Mahatma', 'Gandhi')]
max(people, key=lambda x: x[1])
>>>('Oprah', 'Winfrey')

11

max函数是如何工作的?

它在可迭代对象中寻找“最大”的项。我假设您知道什么是可迭代对象,但如果不知道,那就是您可以循环遍历的东西,比如列表或字符串。

max函数中关键字key有什么用?我知道它也在sort函数的上下文中使用

Key是一个lambda函数,它会告诉max哪些可迭代对象更大。比如说,如果你要排序自己创建的一些对象,而不是像整数这样的明显对象。

lambda表达式的含义?如何阅读它们?它们是如何工作的?

这是一个比较复杂的问题。简单地说,lambda是一个可以“传递”的函数,其他代码可以使用它。例如:

def sum(a, b, f):
    return (f(a) + f(b))

这个函数需要两个对象ab,以及一个函数f。 它会对每个对象调用f(),然后将它们相加。看一下这个调用:

>>> sum(2, 2, lambda a:  a * 2)
8

sum()接受2,并对其调用lambda表达式。 因此,f(a)变为2 * 2,即4。然后它也对b执行相同的操作,并将两者相加。

不那么简单地说,lambda来自λ演算,这是一种返回函数的函数的概念; 它是一种非常酷的数学概念,用于表示计算。 您可以在这里阅读有关该主题的更多信息,然后在这里实际理解它。

最好再多了解一下这个问题,因为lambda可能会令人困惑,而且它们的实用性不是立即显而易见的。 在这里查看更多信息。


6

max 是一个内置函数,它的第一个参数是一个 iterable(如列表或元组)。

关键字参数 key 默认值为 None,但可以接受一个函数来评估,将其视为一个基于函数的包装器,根据函数来评估 iterable。

考虑以下示例字典:

d = {'aim':99, 'aid': 45, 'axe': 59, 'big': 9, 'short': 995, 'sin':12, 'sword':1, 'friend':1000, 'artwork':23}

示例:

>>> max(d.keys())
'sword'

如您所见,如果只传递可迭代对象而没有关键字参数(即key函数),它将返回关键字中的最大值(按字母顺序)。

例如,您可能需要根据关键字的长度而不是按字母表顺序找到最大的关键字:

>>>max(d.keys(), key=lambda x: len(x))
'artwork'

在这个例子中,lambda函数返回将被迭代的键的长度,因此在评估值时,它将跟踪键的最大长度,并返回具有最大长度的键。
例如。
>>> max(d.keys(), key=lambda x: d[x])
'friend'

在这个例子中,lambda函数返回具有最大值的对应字典键的值。

6
根据文档max(iterable[, key])
max(arg1, arg2, *args[, key])
返回可迭代对象中的最大项或两个或多个参数中的最大项。
如果提供一个位置参数,则可迭代对象必须是非空可迭代对象(例如非空字符串、元组或列表)。将返回可迭代对象中的最大项。如果提供了两个或更多位置参数,则返回位置参数中的最大值。
可选的key参数指定类似于用于list.sort()的一参数排序函数。如果提供了key参数,则必须以关键字形式给出(例如,max(a,b,c,key=func))。
这意味着在您的情况下,您提供了一个列表,即players。然后,max函数将遍历列表中的所有项并将它们相互比较以获得“最大值”。
可以想象,对于像player这样的复杂对象,确定其用于比较的值是棘手的,因此您可以使用key参数来确定max函数将如何决定每个player的值。在这种情况下,您使用lambda函数表示“对于players中的每个p,获取p.totalscore并将其用作比较值”。

0
这帮助我理解了这个模式的工作原理。把关键字想象成一个转换函数/比较指标。
max(data, lambda x: transform_function(x))

这是什么意思?想象一下,我们有一个数字列表[1, 2, 3, 4, 5]。我们的任务是找到使正弦函数结果最大的值。
我们不是将max函数应用于数字列表,而是使用我们的转换函数(这里是sin函数)来转换每个值。
1 -> sin(1)
2 -> sin(2)
3 -> sin(3)
4 -> sin(4)
5 -> sin(5)

现在,我们对转换后的值应用max函数。这将给我们sin(2),这是由输入2得到的,所以max(numbers, lambda x: sin(x))将返回2。
让我们将这个想法应用到一个更复杂的情况。考虑以下字典(例如,包含三个温度测量的城市):
cities = {
    "New York": [10, 12, 13],
    "Los Angeles": [14, 15, 16],
    "Washington": [8, 5, -1],
}

我们的目标是找到最高温度的城市(应该是洛杉矶)。我们需要应用什么样的转换呢?简单来说:我们想要看到温度的最大值 = 城市[城市],因此 max(城市[城市]) 应该是我们的转换。所以我们需要进行以下操作。
max(cities, key=lambda city: max(cities[city]))

你甚至可以用嵌套字典来做到这一点。
cities: dict = {
    "New York": {
        "temperatures": [1, 2, 9],
        "populations": [10000, 20000, 30000],
    },
    "Los Angeles": {
        "temperatures": [4, 5, 4],
        "populations": [40000, 50000, 60000],
    },
    "Washington": {
        "temperatures": [1, 0, -1],
        "populations": [10000, 1000, 0],
    }
}
    
max(cities, key=lambda city: max(cities[city]["populations"]))

0
假设来到这个页面的人实际上想知道 len() 中的 key= 是什么,这里是简单的答案: len() 计算对象的长度。如果我们在 min()max() 中指定 len 作为键函数,它将根据它们的长度返回最小/最大项。
food = ['bread', 'tea', 'banana', 'kiwi', 'tomato']

print(max(food, key=len))   # banana
print(min(food, key=len))   # tea

-1
if key not in self.keyStore:
    self.keyStore[key] = []

self.keyStore[key].append([value, timestamp])

如果您无法访问集合或者希望以更中立的方式解决问题,那么在进行编程面试时,上述代码完全有效且易于阅读。

这并没有改进接受的答案 - 同样,这个回答如何回答问题? - Amegon

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