Python:List Comprehensions中的Lambda函数

时间:2011-05-20 18:39:00

标签: python

为什么以下两个列表推导的输出不同,即使f和lambda函数相同?

f = lambda x: x*x
[f(x) for x in range(10)]

[lambda x: x*x for x in range(10)]

请注意,类型(f)和类型(lambda x:x * x)都返回相同的类型。

6 个答案:

答案 0 :(得分:194)

第一个创建一个lambda函数并调用它十次。

第二个不调用该函数。它创建了10种不同的lambda函数。它将所有这些放在一个列表中。使它等同于你需要的第一个:

[(lambda x: x*x)(x) for x in range(10)]

或者更好:

[x*x for x in range(10)]

答案 1 :(得分:69)

这个问题涉及“着名的”和“明显的”Python语法的一个非常臭的部分 - 优先级,lambda或列表理解的for。

我不认为OP的目的是生成从0到9的正方形列表。如果是这样的话,我们可以提供更多的解决方案:

squares = []
for x in range(10): squares.append(x*x)
  • 这是命令式语法的好方法。

但这不是重点。重点是W(hy)TF是这个模糊的表达所以反直觉吗?最后我有一个愚蠢的案例,所以不要过早地解答我的答案(我在面试时就已经过了)。

所以,OP的理解返回了一个lambdas列表:

[(lambda x: x*x) for x in range(10)]

这当然只是10个不同的平方函数副本,请参阅:

>>> [lambda x: x*x for _ in range(3)]
[<function <lambda> at 0x00000000023AD438>, <function <lambda> at 0x00000000023AD4A8>, <function <lambda> at 0x00000000023AD3C8>]

注意 lambda的内存地址 - 它们都不同!

你当然可以拥有这个表达式的更“最佳”(haha)版本:

>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]

请参阅? 3次相同的 lambda。

请注意,我使用_作为for变量。它与x中的lambda无关(它在词汇上黯然失色!)。得到它?

我要省略讨论,为什么语法优先权不是这样,这一切都意味着:

[lambda x: (x*x for x in range(10))]

可以是:[[0, 1, 4, ..., 81]],或[(0, 1, 4, ..., 81)],或我认为最符合逻辑的,这将是{1}}的1个元素 - 一个{{ 1}}返回值。事实并非如此,语言不会这样。

但是什么,如果......

如果你没有掩盖list变量,并在你的generator中使用它,该怎么办?

嗯,那么废话就发生了。看看这个:

for

这当然意味着:

lambda

但这并不意味着:

[lambda x: x * i for i in range(4)]

这太疯狂了!

列表理解中的lambdas是对这种理解范围的封闭。一个词汇闭包,所以它们引用[(lambda x: x * i) for i in range(4)] via引用,而不是它们在评估时的值!

所以,这个表达式:

[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]

大致等同于:

i

我确信我们可以在这里看到更多使用python反编译器(我的意思是例如[(lambda x: x * i) for i in range(4)] 模块),但对于Python-VM不可知的讨论,这就足够了。 求职面试的问题非常多。

现在,如何制作一个[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)] 乘数lambdas,它真正乘以连续的整数?好吧,与接受的答案类似,我们需要通过将其包含在另一个dis中来打破与list的直接联系,这将被称为 in 列表推导表达式:< / p>

在:

i

后:

lambda

(我的外部lambda变量也是>>> a = [(lambda x: x * i) for i in (1, 2)] >>> a[1](1) 2 >>> a[0](1) 2 ,但我认为这是更清晰的解决方案 - 我引入>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)] >>> a[1](1) 2 >>> a[0](1) 1 以便我们都可以看到哪个女巫是哪个。

答案 2 :(得分:17)

最大的区别是第一个例子实际上调用了lambda f(x),而第二个例子却没有。{/ p>

您的第一个示例相当于[(lambda x: x*x)(x) for x in range(10)],而您的第二个示例相当于[f for x in range(10)]

答案 3 :(得分:7)

第一个

f = lambda x: x*x
[f(x) for x in range(10)]

为范围中的每个值运行f(),因此每个值都为f(x)

第二个

[lambda x: x*x for x in range(10)]

为列表中的每个值运行lambda,因此它会生成所有这些函数。

答案 4 :(得分:4)

人们给出了很好的答案,但忘记提及我认为最重要的部分: 在第二个示例中,列表推导的XX函数的lambda不同,它们完全不相关。 所以第二个例子实际上与:

相同
[Lambda X: X*X for I in range(10)]

range(10)上的内部迭代只负责在列表中创建10个类似的lambda函数(10个独立的函数但完全相似 - 返回每个输入的幂2)。

另一方面,第一个示例完全不同,因为迭代的X与结果交互,每次迭代的值为X*X,因此结果为[0,1,4,9,16,25, 36, 49, 64 ,81]

答案 5 :(得分:2)

其他答案是正确的,但是如果你试图制作一个功能列表,每个功能都有一个不同的参数,可以在以后执行,那么下面的代码就会这样做:

import functools
a = [functools.partial(lambda x: x*x, x) for x in range(10)]

b = []
for i in a:
    b.append(i())

In [26]: b
Out[26]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

虽然这个例子是设计的,但是当我想要一个每个打印不同的函数列表时,我发现它很有用,即

import functools
a = [functools.partial(lambda x: print(x), x) for x in range(10)]

for i in a:
    i()