Python:List Comprehensions中的Lambda函数

use*_*191 125 python

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

f = lambda x: x*x
[f(x) for x in range(10)]
Run Code Online (Sandbox Code Playgroud)

[lambda x: x*x for x in range(10)]
Run Code Online (Sandbox Code Playgroud)

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

Win*_*ert 227

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

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

[(lambda x: x*x)(x) for x in range(10)]
Run Code Online (Sandbox Code Playgroud)

或者更好的是:

[x*x for x in range(10)]
Run Code Online (Sandbox Code Playgroud)

  • 或者`map(lambda x:x*x,range(10))`,这可能是OP首先意味着的. (8认同)
  • @DanielRoseman,或者准确地说 `list(map(lambda x: x*x, range(10)))` 会给你 `[0, 1, 4, 9, 16, 25, 36, 49, 64, 81 ]` (4认同)

Tom*_*dor 81

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

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

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

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

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

[(lambda x: x*x) for x in range(10)]
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

>>> [lambda x: x*x] * 3
[<function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>, <function <lambda> at 0x00000000023AD2E8>]
Run Code Online (Sandbox Code Playgroud)

看到?3次相同的 lambda.

请注意,我用_for变量.它没有任何跟xlambda(它在词法黯然失色!).得到它?

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

[lambda x: (x*x for x in range(10))]
Run Code Online (Sandbox Code Playgroud)

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

但是,如果......

如果你不掩饰for变量怎么办,并在你的lambdas中使用它?

好吧,然后发生了废话.看这个:

[lambda x: x * i for i in range(4)]
Run Code Online (Sandbox Code Playgroud)

这当然意味着:

[(lambda x: x * i) for i in range(4)]
Run Code Online (Sandbox Code Playgroud)

但它并不意味着:

[(lambda x: x * 0), (lambda x: x * 1), ... (lambda x: x * 3)]
Run Code Online (Sandbox Code Playgroud)

这太疯狂了!

列表理解中的lambdas是对这种理解范围的封闭.一个词汇封闭,因此它们指的是i通过引用,而不是它们在评估时的价值!

所以,这个表达式:

[(lambda x: x * i) for i in range(4)]
Run Code Online (Sandbox Code Playgroud)

大致等同于:

[(lambda x: x * 3), (lambda x: x * 3), ... (lambda x: x * 3)]
Run Code Online (Sandbox Code Playgroud)

我相信我们可以在这里看到更多使用python反编译器(我的意思是例如dis模块),但对于Python-VM不可知的讨论,这就足够了.求职面试的问题非常多.

现在,如何制作list乘数lambdas,它真正乘以连续的整数?好吧,类似于接受的答案,我们需要i通过将其包装在另一个中来打破直接联系lambda,这将列表理解表达式中被调用:

之前:

>>> a = [(lambda x: x * i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
2
Run Code Online (Sandbox Code Playgroud)

后:

>>> a = [(lambda y: (lambda x: y * x))(i) for i in (1, 2)]
>>> a[1](1)
2
>>> a[0](1)
1
Run Code Online (Sandbox Code Playgroud)

(我还有外部lambda变量= i,但我认为这是更清晰的解决方案 - 我介绍了y所以我们都可以看到哪个女巫是哪个).

  • 这是一个残酷和不寻常的面试问题. (17认同)
  • 哇。我只是被这种荒谬的行为所咬。谢谢你的文章! (2认同)
  • 这应该成为*可接受的答案。 (2认同)
  • 为了清晰和完整起见,您可以编写最后一个列表理解,例如:“[partial(lambda i, x: i * x, i) for i in (1, 2)]” (2认同)
  • 出于多种目的,我认为有一种 python 内部方法可以解决它:具有默认值的 Lambda。这应该有效: `[lambda x, i=i: x * i for i in range(4)]` https://docs.python.org/3.4/faq/programming.html#why-do-lambdas-defined -in-a-loop-with- different-values-all-return-the-same-result 。如果您不介意额外的可重写参数,那就太好了。 (2认同)

Gab*_*abe 18

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

你的第一个例子相当于[(lambda x: x*x)(x) for x in range(10)]你的第二个例子相当于[f for x in range(10)].


zel*_*lio 9

第一个

f = lambda x: x*x
[f(x) for x in range(10)]
Run Code Online (Sandbox Code Playgroud)

运行f()范围中的每个值,以便f(x)为每个值执行

第二个

[lambda x: x*x for x in range(10)]
Run Code Online (Sandbox Code Playgroud)

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


she*_*idp 7

其他的答案是正确的,但如果你试图使功能列表,每一个不同的参数,可以被执行,下面的代码将做到这一点:

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]
Run Code Online (Sandbox Code Playgroud)

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

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

for i in a:
    i()
Run Code Online (Sandbox Code Playgroud)


小智 6

人们给出了很好的答案,但忘记了我认为最重要的部分:在第二个示例X中,列表理解Xlambda功能与函数的不同,它们是完全无关的。因此,第二个示例实际上与以下示例相同:

[Lambda X: X*X for I in range(10)]
Run Code Online (Sandbox Code Playgroud)

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

在另一方面,第一个例子中的作品完全不同,因为重复的X与结果进行交互DO,每次迭代的值是X*X这样的结果将是[0,1,4,9,16,25, 36, 49, 64 ,81]