有意思的题目

题目:写出下面程序运行结果

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:1097524789

def multipliers():
    return [lambda x:i*x for i in range(4)]
print([m(2) for m in multipliers()])
一道 3 行代码的 Python面试题,我懵逼了一天 Python 第1张

正确答案:

[6,6,6,6]
一道 3 行代码的 Python面试题,我懵逼了一天 Python 第2张

第一眼看,不就是匿名函数吗?但是仔细想想匿名函数在平时的开发中没怎么用,所以也忘的差不多了。例如那个m(2)当时就不懂是啥意思了。

好,我们就来看看这个题目,首先第一段代码:

def multipliers():
    return [lambda x:i*x for i in range(4)]
一道 3 行代码的 Python面试题,我懵逼了一天 Python 第3张

这是一个典型的列表推导式,简而言之就是在列表中推导计算并且将计算的结果放入列表,上面这串代码我们可以写成:

squares = []
for i in range(4):
    res = lambda x:i*x
    squares.append(res)
一道 3 行代码的 Python面试题,我懵逼了一天 Python 第4张

这样可能看的更加的简单明了。但是这里面也有一个匿名函数,计算i*x的值,如果大家对匿名函数不太懂的,可以去翻阅相关资料了解一下。

我们接下来看这段代码:

print([m(2) for m in multipliers()])
一道 3 行代码的 Python面试题,我懵逼了一天 Python 第5张

其实这段代码也很好理解,m(2)的意思就是将2作为参数传入上面的匿名函数当中,但是为什么结果等于[6,6,6,6]呢?

 

我们来debug一下看看:

一道 3 行代码的 Python面试题,我懵逼了一天 Python 第6张一道 3 行代码的 Python面试题,我懵逼了一天 Python 第7张

断点,开始debug

一道 3 行代码的 Python面试题,我懵逼了一天 Python 第8张一道 3 行代码的 Python面试题,我懵逼了一天 Python 第9张

跳到了multipliers函数当中

一道 3 行代码的 Python面试题,我懵逼了一天 Python 第10张一道 3 行代码的 Python面试题,我懵逼了一天 Python 第11张

仍然在循环体中

一道 3 行代码的 Python面试题,我懵逼了一天 Python 第12张一道 3 行代码的 Python面试题,我懵逼了一天 Python 第13张

一道 3 行代码的 Python面试题,我懵逼了一天 Python 第14张一道 3 行代码的 Python面试题,我懵逼了一天 Python 第15张

运行到3,循环即将结束

一道 3 行代码的 Python面试题,我懵逼了一天 Python 第16张一道 3 行代码的 Python面试题,我懵逼了一天 Python 第17张

跳转下来

一道 3 行代码的 Python面试题,我懵逼了一天 Python 第18张一道 3 行代码的 Python面试题,我懵逼了一天 Python 第19张

重新进入匿名函数计算

一道 3 行代码的 Python面试题,我懵逼了一天 Python 第20张一道 3 行代码的 Python面试题,我懵逼了一天 Python 第21张

计算

这里我省略了几张图,因为结果都是i=3 ,x=2,所以相乘自然为6。

相信大家看到这里仍然有很多的疑问,为什么i最后等于3?

其实这里涉及到闭包函数的概念,什么是闭包函数呢?

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

def multipliers():
  squares = []
  for i in range(4):
      res = lambda x:i*x
      squares.append(res)
一道 3 行代码的 Python面试题,我懵逼了一天 Python 第22张

我们用这种方法来看待这个闭包函数lambda x:i*x,为什么称它为闭包函数呢?因为当执行lambda x:i*x这串代码时调用了上一层函数multipliers()的局部命名空间的变量i,所以此处是闭包函数。

但是此时的列表中并不是匿名函数计算出来的值,因为此时还没有被调用,此时列表中只是有四个匿名函数的内存地址:

一道 3 行代码的 Python面试题,我懵逼了一天 Python 第23张一道 3 行代码的 Python面试题,我懵逼了一天 Python 第24张

[<function multipliers.<locals>.<lambda> at 0x1057d1710>, <function multipliers.<locals>.<lambda> at 0x10586dd40>, <function multipliers.<locals>.<lambda> at 0x10586de60>, <function multipliers.<locals>.<lambda> at 0x10586df80>]
一道 3 行代码的 Python面试题,我懵逼了一天 Python 第25张

那什么时候才算真正调用呢?在上面我们讲过

print([m(2) for m in multipliers()])
一道 3 行代码的 Python面试题,我懵逼了一天 Python 第26张

这串代码将2作为参数传入上面的匿名函数当中,此时也就是调用了匿名函数。当去调用的时候,for i in range(4)这个循环已经执行完毕,此时的i已经是3了,所以就有了最后的结果:

[6,6,6,6
一道 3 行代码的 Python面试题,我懵逼了一天 Python 第27张

 

总结

其实很多题目都是出自一些我们平时不是很重视的知识点上,但是往往这些知识点非常重要。所以学习的时候,对于重点知识不能因为不使用它而去遗忘,而是要多去看看。非常感谢大家能够看完这篇文章......

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄