在Python的lambda表达式中可以有多个语句吗?

160 投票
22 回答
239445 浏览
提问于 2025-04-15 11:35

我有一个列表,里面又包含了很多小列表:

lst = [[567, 345, 234], [253, 465, 756, 2345], [333, 777, 111, 555]]

我想把这些小列表里的第二小的数字提取出来,最后得到的结果应该是 [345, 465, 333]

如果我只想要最小的数字,我可以这样写:map(lambda x: min(x), lst)。但是要得到第二小的数字,我想到了先排序再取出结果中的第二个数字,像这样:map(lambda x: sort(x)[1], lst)。不过,sort 这个函数会 返回 None,而不是直接返回排序后的列表

如果可以在 lambda 里写多个语句,我就可以写成 map(lambda x: sort(x); x[1], lst),但这是不允许的。

我能否用 map 来解决这个问题,而不需要定义一个命名的函数?怎么做呢?

22 个回答

37

时间旅行者来了。如果你想在一个lambda表达式里写多个语句,可以把其他的lambda表达式作为参数传给这个lambda。

(lambda x, f: list((y[1] for y in f(x))))(lst, lambda x: (sorted(y) for y in x))

其实你不能直接写多个语句,但可以通过把lambda传给lambda来模拟这种效果。

编辑:时间旅行者又回来了!你还可以利用布尔表达式的特性(记得短路规则和真假值)来连接操作。使用三元运算符会让你有更多的选择。再次强调,你不能有多个语句,但当然可以有很多函数调用。这个例子用一些随意的数据做了一些操作,但它展示了你可以做一些有趣的事情。打印语句是返回None的函数示例(.sort()方法也是这样),但它们也帮助展示了这个lambda在做什么。

>>> (lambda x: print(x) or x+1)(10)
10
11
>>> f = (lambda x: x[::2] if print(x) or x.sort() else print(enumerate(x[::-1]) if print(x) else filter(lambda (i, y): print((i, y)) or (i % 3 and y % 2), enumerate(x[::-1]))))
>>> from random import shuffle
>>> l = list(range(100))
>>> shuffle(l)
>>> f(l)
[84, 58, 7, 99, 17, 14, 60, 35, 12, 56, 26, 48, 55, 40, 28, 52, 31, 39, 43, 96, 64, 63, 54, 37, 79, 25, 46, 72, 10, 59, 24, 68, 23, 13, 34, 41, 94, 29, 62, 2, 50, 32, 11, 97, 98, 3, 70, 93, 1, 36, 87, 47, 20, 73, 45, 0, 65, 57, 6, 76, 16, 85, 95, 61, 4, 77, 21, 81, 82, 30, 53, 51, 42, 67, 74, 8, 15, 83, 5, 9, 78, 66, 44, 27, 19, 91, 90, 18, 49, 86, 22, 75, 71, 88, 92, 33, 89, 69, 80, 38]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
(0, 99)
(1, 98)
(2, 97)
(3, 96)
(4, 95)
(5, 94)
(6, 93)
(7, 92)
(8, 91)
(9, 90)
(10, 89)
(11, 88)
(12, 87)
(13, 86)
(14, 85)
(15, 84)
(16, 83)
(17, 82)
(18, 81)
(19, 80)
(20, 79)
(21, 78)
(22, 77)
(23, 76)
(24, 75)
(25, 74)
(26, 73)
(27, 72)
(28, 71)
(29, 70)
(30, 69)
(31, 68)
(32, 67)
(33, 66)
(34, 65)
(35, 64)
(36, 63)
(37, 62)
(38, 61)
(39, 60)
(40, 59)
(41, 58)
(42, 57)
(43, 56)
(44, 55)
(45, 54)
(46, 53)
(47, 52)
(48, 51)
(49, 50)
(50, 49)
(51, 48)
(52, 47)
(53, 46)
(54, 45)
(55, 44)
(56, 43)
(57, 42)
(58, 41)
(59, 40)
(60, 39)
(61, 38)
(62, 37)
(63, 36)
(64, 35)
(65, 34)
(66, 33)
(67, 32)
(68, 31)
(69, 30)
(70, 29)
(71, 28)
(72, 27)
(73, 26)
(74, 25)
(75, 24)
(76, 23)
(77, 22)
(78, 21)
(79, 20)
(80, 19)
(81, 18)
(82, 17)
(83, 16)
(84, 15)
(85, 14)
(86, 13)
(87, 12)
(88, 11)
(89, 10)
(90, 9)
(91, 8)
(92, 7)
(93, 6)
(94, 5)
(95, 4)
(96, 3)
(97, 2)
(98, 1)
(99, 0)
[(2, 97), (4, 95), (8, 91), (10, 89), (14, 85), (16, 83), (20, 79), (22, 77), (26, 73), (28, 71), (32, 67), (34, 65), (38, 61), (40, 59), (44, 55), (46, 53), (50, 49), (52, 47), (56, 43), (58, 41), (62, 37), (64, 35), (68, 31), (70, 29), (74, 25), (76, 23), (80, 19), (82, 17), (86, 13), (88, 11), (92, 7), (94, 5), (98, 1)]
106

把表达式放在一个列表里,可以模拟出多个表达式的效果:

比如:

lambda x: [f1(x), f2(x), f3(x), x+1]

不过,这种方法不适用于语句。

170

这里有几个不同的回答可以给你,从你具体的问题到更一般的关注点。我们从最具体的开始说:

问:可以在一个lambda表达式中放多个语句吗?

答:不可以。不过你其实不需要用lambda。你可以把这些语句放在一个def函数里。也就是说:

def second_lowest(l):
    l.sort()
    return l[1]

map(second_lowest, lst)

问:可以通过排序列表来从lambda中获取第二小的项吗?

答:可以。正如alex的回答所指出的,sorted()是一个排序的版本,它会创建一个新列表,而不是在原地排序,并且可以进行链式调用。注意,这可能是你应该使用的方法——在你的映射中对原始列表产生副作用是不好的做法。

问:我应该如何从一系列列表中获取每个列表的第二小项?

答:sorted(l)[1]其实不是最好的方法。它的复杂度是O(N log(N)),而且存在O(n)的解决方案。这可以在heapq模块中找到。

>>> import  heapq
>>> l = [5,2,6,8,3,5]
>>> heapq.nsmallest(l, 2)
[2, 3]

所以只需使用:

map(lambda x: heapq.nsmallest(x,2)[1],  list_of_lists)

通常使用列表推导式会更清晰,这样就完全避免了使用lambda:

[heapq.nsmallest(x,2)[1] for x in list_of_lists]

撰写回答