说说我对[lambda x: x*i for i in range(4)]的理解

说说我对[lambda x: x*i for i in range(4)]的理解

  • 前言
    这段代码早前在群里看到过,直接上图
    def multipliers():
        return [lambda x:i*x for i in range(4)]
    print([m(2) for m in multipliers()])
    
    第一眼看,不就是匿名函数吗?不就是列表推导式吗?装神弄鬼,答案简单。列表推导式里循环遍历4次,遍历结束return 出来的就是4个函数引用实例,并且i分别为 [0, 1, 2, 3 ],print中将对4个实例进行调用,那么答案就出来了 [0 * 2, 1 * 2, 2 * 2, 3 * 2] = [0, 2, 4, 6]
  • 为什么要发博文
    这里我停顿一下,当然不是为了卖关子,既然大家冲浪来找这个问题,当然是自己在本地运行后对结果有所疑惑了。那我之所以想说说我的理解,是因为在网上看到太多对这个问题的说法了。有的网友说是 闭包 导致的,有的网友说是python的 延迟绑定 导致的,还有说 lambda函数签名 导致的等等等等。带着这些看法我们继续往下说
  • 本地执行此函数
    在这里插入图片描述
    结果出乎意料,[6, 6, 6, 6]。我第一反应下的答案显然错了,想得太简单了
  • 用公司小伙的话,我们OB一波
    这是一个典型的列表推导式,简而言之就是在列表中推导计算并且将计算的结果放入列表,上面这串代码我们可以写成:
    def multipliers():
        instance_list = []
        for item in range(4):
            res = lambda x: item * x
        	instance_list.append(res)
        return instance_list
     
    print([func(2) for func in multipliers()])  
    
    执行简化的这段代码 在这里插入图片描述
    只是简化了代码,结果不变。那我们继续操作,将代码从函数里拿出来,看看结果
    在这里插入图片描述
    我们也看到了结果,依然是 [6, 6, 6, 6]。前面我们说过这可能是 闭包函数 的局部变量导致的,此处确实为闭包函数,因为当执行 lambda x:i*x 这串代码时调用了上一层函数multipliers()的局部命名空间的变量i,所以此处是闭包函数。但真的是这个原因么,显然不是。在去除外层函数multipliers后,已经失去了闭包函数的结构了,但是两次执行的结果相同。由此可知结论:不是闭包函数导致的,得出结论后继续往下看
    在这里插入图片描述
    继续简化代码,将lambda函数替换成普通函数,得出结果依然是 [6, 6, 6, 6]。由此,得到结论 不是lambda函数导致的
  • 如此结果的原理是什么呢?
    我们以最简化版的代码为例,加以说明 在这里插入图片描述
    就像上面所说的:四次循环中外层函数命名空间中的 i 从 0–>1–>2–>3 最后固定为3,而在此过程中内嵌函数中因为没有定义 i 所以只有Lambda 函数动态运行时,在自己命名空间中找不到 i 才去外层函数复制 i = 3 过来,结果就是所有内嵌函数的 i 都为 3,导致得不到预计输出结果:[0,2,4,6] 只能得到 [3, 3, 3, 3]

好了,去收拾了。没完全整完,还有涉及到的解决方法没说,还有 延迟绑定 没分析,哪天时间多,接着来

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值