Weird behavior: Lambda inside list comprehension
在python 2.6中:
1 | [x() for x in [lambda: m for m in [1,2,3]]] |
结果是:
1 | [3, 3, 3] |
我希望输出为[1、2、3]。 即使使用非列表理解方法,我也会遇到完全相同的问题。 甚至在我将m复制到另一个变量之后。
我想念什么?
为了使Lambda记住
1 2 | [x() for x in [lambda m=m: m for m in [1,2,3]]] # [1, 2, 3] |
之所以可行,是因为默认值在定义时设置一次。现在,每个lambda都使用自己的默认值
您遇到的效果称为闭包,当您定义一个引用非局部变量的函数时,该函数将保留对该变量的引用,而不是获取其自己的副本。为了说明这一点,我将把您的代码扩展为等效版本,而无需任何理解或lambda。
1 2 3 4 5 | inner_list = [] for m in [1, 2, 3]: def Lambda(): return m inner_list.append(Lambda) |
因此,此时,
1 2 3 | outer_list = [] for x in inner_list: outer_list.append(x()) |
特别是,由于内部列表是在外部列表开始构建之前完全构建的,因此
长话短说,您不想这样做。更具体地说,您遇到的是操作顺序问题。您正在创建三个单独的
-评论-
1 2 | >>> [lambda: m for m in range(3)] [<function <lambda> at 0x021EA230>, <function <lambda> at 0x021EA1F0>, <function <lambda> at 0x021EA270>] |
那是三个单独的lambda。
并且,作为进一步的证据:
1 2 | >>> [id(m) for m in [lambda: m for m in range(3)]] [35563248, 35563184, 35563312] |
同样,三个单独的ID。
查看功能的
1 2 3 4 5 | >>> print(*[x.__closure__[0] for x in [lambda: m for m in [1,2,3]]], sep=' ') <cell at 0x00D17610: int object at 0x1E2139A8> <cell at 0x00D17610: int object at 0x1E2139A8> <cell at 0x00D17610: int object at 0x1E2139A8> |
如果您不希望函数将m作为关键字参数,按照unubtu的回答,则可以在每次迭代时使用一个附加的lambda来评估m:
1 2 | >>> [x() for x in [(lambda x: lambda: x)(m) for m in [1,2,3]]] [1, 2, 3] |
我个人认为这是一个更优雅的解决方案。 Lambda返回一个函数,因此如果我们要使用该函数,则应该使用它。在lambda中的"匿名"变量和生成器中使用相同的符号会造成混淆,因此在我的示例中,我使用了不同的符号以使其更清晰。
1 2 3 | >>> [ (lambda a:a)(i) for i in range(3)] [0, 1, 2] >>> |
它也更快。
1 2 3 4 5 | >>> timeit.timeit('[(lambda a:a)(i) for i in range(10000)]',number=10000) 9.231263160705566 >>> timeit.timeit('[lambda a=i:a for i in range(10000)]',number=10000) 11.117988109588623 >>> |
但不如地图快:
1 2 | >>> timeit.timeit('map(lambda a:a, range(10000))',number=10000) 5.746963977813721 |
(我不止一次地运行了这些测试,结果是相同的,这是在python 2.7中完成的,在python 3中的结果是不同的:两个列表的理解在性能上更加接近,并且两者都慢得多,map仍然快得多。)
我也注意到了。我得出的结论是,lambda仅创建一次。因此,实际上,您的内部列表推导将给出3个与m的最后一个值都有关的相同函数。
试试看并检查元素的id()。
[注意:此答案不正确;查看评论]