Python:List Comprehensions中的Lambda函数

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

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

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

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

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

第二个不叫这个函数。 它创build10个不同的lambda函数。 它把所有这些放在一个列表中。 为了使它相当于你需要的第一个:

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

或者更好的是:

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

这个问题触及了“着名”和“显而易见的”Python语法的一个非常糟糕的部分 – 优先,lambda或for列表理解。

我不认为OP的目的是生成一个从0到9的方块列表。如果是这样的话,我们可以给出更多的解决scheme:

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

但这不是重点。 问题是W(hy)TF是这个模棱两可的expression,所以反直觉? 最后,我还有一个愚蠢的例子,所以不要太早拒绝我的回答(我在面试时曾经这样做过)。

所以,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的内存地址 – 它们都是不同的!

你当然可以有一个更“优化”(哈哈)版本的这个expression式:

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

看到? 3次相同的 lambda。

请注意,我用_作为variables。 它与lambdax没有任何关系(它是词法上的阴影!)。 得到它?

我正在讨论这个问题,为什么语法的优先顺序不是这样,这一切意味着:

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

这可能是[[0, 1, 4, ..., 81]][(0, 1, 4, ..., 81)] ,或者我认为最合理的 ,这将是1个元素 – 一个generator返回值。 事实并非如此,语言不是这样工作的。

但是什么,如果…

如果你没有掩盖forvariables,并在你的lambda s中使用它?

那么,垃圾发生。 看这个:

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

这当然意味着:

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

但它并不意味着:

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

这太疯狂了!

列表理解中的lambdas是关于这个理解范围的封闭。 一个词汇封闭,所以他们指的是i通过参考,而不是它的价值,当他们被评估!

所以,这个expression式:

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

大致等同于:

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

我相信我们可以在这里看到更多使用python反编译器(我的意思是dis模块),但是对于Python-VM-agnostic的讨论,这已经足够了。 面试的问题非常多。

现在,如何制作一个乘法器lambdas list ,真正乘以连续的整数? 那么,与被接受的答案类似,我们需要通过将它包装到另一个lambda ,这个lambda在列表理解expression式中被调用,

之前:

 >>> 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 

(我的外层lambdavariables也是= i ,但我决定这是更清晰的解决scheme – 我介绍了y以便我们都可以看到哪个女巫是哪个)。

最大的区别是第一个例子实际上调用lambda f(x) ,而第二个例子不是。

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

第一个

 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,以便生成所有这些函数。

人们给出了很好的答案,但忘了提到我认为最重要的部分:在第二个例子中,列表理解的Xlambda函数的X不是一样的,它们是完全不相关的。 所以第二个例子实际上是一样的:

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

range(10)的内部迭代只负责在列表中创build10个类似的lambda函数(10个独立的函数,但完全相似 – 返回每个input的功率2)。

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

其他的答案是正确的,但是如果你正在尝试做一个函数列表,每个函数都有一个不同的参数,那么以后可以执行下面的代码:

 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()