[m(2) for m in multipliers()]-------面试题

好久没有用过 CSDN 了, 一用就有这个文章的问题,有毒啊。我觉得我的确很菜(就是闭包,不接受反驳,谢谢)

闭包是后期绑定,  解决后期绑定问题。

建议把下面这段代码打断点看看,每次循环返回的是个函数,循环结束后 i 就是  3 .调用的结果

[0, 2, 4, 6]

如果去掉这个参数,立马就是 6  了。反驳的好好看看闭包的基础知识吧,定义。球球了

def multipliers():
    squares = []
    for i in range(4):
        res = lambda x, i=i: i*x
        squares.append(res)
    return squares
print([m(2) for m in multipliers()])

或者

def multipliers():
    return [lambda x, i=i:i*x for i in range(4)]
print([m(2) for m in multipliers()])

通过默认参数绑定他的参数。

这样就能解决闭包的后期绑定问题

2020-08-13  更新

这个文章写了很久了,然后最近有人评论说不是闭包的原因

ipeiyuan666:和闭包没关系吧, 主要是执行函数的时机, 它返回的是一个列表, 再遍历列表执行里面的函数, 而此时for i in range(4)已经执行完, i已经为3了, 所以只能拿3来计算 可以把列表改为括号,相当于返回迭代器,遍历时i为几就执行几,执行一个yield一个,大概是这样, 我的理解

weixin_42316448:大哥,这里就是因为没有闭包,才会是那个结果的,如果有了闭包,还会是这个结果?

weixin_42316448:大哥,如果存在闭包就不是这个结果了,想想清楚好不好 

可能我没说清楚,我找了几篇文章

关于 [lambda x: x*i for i in range(4)] 理解

fun = [lambda x: x*i for i in range(4)] 本质解析/原理,LEGB规则 闭包原理

可以仔细看看。也谢谢你们,让我又看一遍理解更深刻了。

结果变成 0,2, 4,6的话,就不是闭包了,结果是6,6,6,6.就是因为闭包的原因

然后重点看这句话

如果在一个内部函数里,对外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认定是闭包。


def multipliers():
    return [lambda x:i*x for i in range(4)]
print([m(2) for m in multipliers()])

这个题让我涨知识了,分享出来给大家看看,大神跳过。

我这脑子反应较慢,所以呢研究了一下

总共涉及到:
 

1、列表推导式
2、匿名函数
3、闭包函数
4、for循环对函数的迭代调用
5、闭包函数的调用

有点恐怖,简简单单的题这么多知识点

列表推导式 :[x for x in range(10)]这就是个简单的列表推导式,可以复制运行一下,就懂了

匿名函数也知道, 匿名函数的关键字为lambda,表现形式为:lambda 参数 : 返回值,lambda后面的参数就是函数的形参,冒号后面的表达式就是返回值。

闭包函数当前函数引用到上一层函数的局部命名空间的变量时就会触发闭包规则。我们说触发了闭包的函数叫做闭包函数,但是要注意一点:只有当调用闭包函数的时候它才会去引用外层函数的变量,因为在调用闭包函数之前,闭包内部的命名空间还不存在。

#根据前面的叙述,我们可以把它改成容易理解的形式:
def multipliers():
    squares = []
    for i in range(4):
        res = lambda x:i*x
        squares.append(res)
    return squares
print([m(2) for m in multipliers()])

匿名函数使用了外部的变量i,触发了闭包规则

然后函数multipliers()的返回值是一个列表,这个列表的元素为四个闭包函数名指向的内存地址,虽然for i in range(4)这段代码里面的i的值分别被赋予了 0 1 2 3这四个值,但是闭包函数res并没有引用这四个值,因为闭包函数此时此刻还没有被真正调用,列表推导式仅仅是把四个匿名函数指向的内存地址保存在了一个列表里,因为没有调用,所以匿名函数内部的代码并没有执行,也就不存在引用。

所以函数multipliers()的返回值就是这样的一个列表:截图不全

相信到这里大家都明白的差不多了:

 我们来看最后一条语句print([m(2) for m in multipliers()])
for m in multipliers() 这条语句到底干了什么?其实它干的事情只有一个,那就是遍历了函数multipliers()返回的列表,在遍历列表的同时把每个匿名函数赋值给了m,把它拆分来看就是这样:
m = lambda x:i*x
m = lambda x:i*x
m = lambda x:i*x
m = lambda x:i*x
并且每次都执行了一次 m(2),也就是每次都调用了一下匿名函数,注意:此时此刻匿名函数才真正被调用了,然后它会引用外层命名空间的变量i,那么此时i的值是多少呢? 
因为for i in range(4)这个for循环已经执行完毕,i的值等于3,所以每次当执行m(2)时,i的值都等于3
所以每次调用m(2)的结果都是6
最终输出结果为[6, 6, 6, 6]

将它完全拆开

def multipliers():
    # 先执行for循环,j变量更新为4,这个列表只是做了一个调用的工作,所以结果为4个8
    squares = []
    def l(i):
        print('l')
        print(i,j)
        return i * j
    for j in range(5):
        print('for')
        squares.append(l)
    return squares
print([m(2) for m in multipliers()])

这个就很清晰了

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

裸睡的雨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值