似乎只要一沾上for循环,难度立刻加倍,下面我们来看一道python的面试题:

要求写出下面代码的输出结果并且解释原因。

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、闭包函数的调用

首先我们来说一下列表推导式,只有深入理解列表推导式,我们才能理解下面这句话到底干了些什么事情[lambda x:i*x for i in range(4)]
引用官方文档中对于列表推导式的一个例子:squares = [x2 for x in range(10)] 这个列表推导式返回的结果为[0, 1, 4, 9, 16, 25, 36, 49, 64, 81],for循环通过对range(10)进行迭代后得到每个x的值,然后对它进执行x2的操作,最终结果为一个列表
那么如果不用列表推导式如何达到这个目的呢?答案如下,这个列表推导式等同于下面的代码:

squares = []
for x in range(10):squares.append(x**2)

这段代码执行后,squares的结果一样是[0, 1, 4, 9, 16, 25, 36, 49, 64, 81],根据这个例子我们可以简单地认为列表推导式是这样工作的:首先它会定义一个空列表,然后根据设定的条件得到一个一个的元素,同时把元素添加进列表中。

现在回到我们这道题,来看一下本题中的[lambda x:i*x for i in range(4)]这个列表推导式,如果把它拆开来的话它等价于下面的这段代码:

squares = []
for i in range(4):res = lambda x:i*xsquares.append(res)

最终squares就是列表推导式的结果(一个列表),然后我们再研究下这个列表中的元素都是什么。
到这里,如果你明白了,我们就可以继续进行下一步了——理解匿名函数。

匿名函数的关键字为lambda,表现形式为:lambda 参数 : 返回值,lambda后面的参数就是函数的形参,冒号后面的表达式就是返回值。
比如:lambda a, b: a+b 这个简单的匿名函数可以传入两个参数a和b,结果返回a+b,这里要记住,只有调用这个匿名函数,它才会执行冒号后面的代码,这也是函数的执行法则,只有被调用时,函数内部的命名空间才会生效,在被调用之前它就是一个函数名指向的内存地址而已。

匿名函数虽然是匿名的,但是它也可以有名字,也可以作为一个结果赋值给任意的变量,所以它显然可以成为一个函数的返回值,也可以变成一个列表的元素,只不过此时这个列表的元素是匿名函数对应的内存地址罢了。见下面的例子:

#匿名函数直接赋值给变量lam
lam = lambda a,b:a+b
#此时lam指向了匿名函数的内存地址
print(lam)#此时的lam就是一个内存地址:<function <lambda> at 0x7fecdc6b7e18>
res = lam(2,5) #调用匿名函数,把结果赋值给res
print(res)
<function <lambda> at 0x7fecdc6b7e18>
7

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

然后我们回头看这道题的代码:

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

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

我们来看最后一条语句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():return [lambda x:i*x for i in range(4)]
print([m(2) for m in multipliers()])
[6, 6, 6, 6]

把这道面试题中的所有列表推导式拆开的话 它应该是下面这个样子,结果完全一样:

def multipliers():squares = []for i in range(4):res = lambda x:i*xsquares.append(res)return squares#print(multipliers()),此时此刻如果我们打印一下这个函数,也就是调用一下看看返回结果,你会发现,它就是一个由四个函数内存地址组成的列表:
'''[<function multipliers.<locals>.<lambda> at 0x7fecdc6de2f0>, <function multipliers.<locals>.<lambda> at 0x7fecdc6de510>, <function multipliers.<locals>.<lambda> at 0x7fecdc6de158>, <function multipliers.<locals>.<lambda> at 0x7fecdc6de268>]'''squares2 = []
for m in multipliers():squares2.append(m(2))
print(squares2)
[6, 6, 6, 6]

python面试题之“该死的for循环系列”(二)相关推荐

  1. python人脸识别项目_基于Python与命令行人脸识别项目(系列二)

    在knowe_people文件夹中创建blur_faces_on_webcam.py文件并写入以下代码: import face_recognition import cv2 # This is a ...

  2. 一道Python面试题,据说大部分人都中招了,纷纷开始怀疑自己

    无意间,看到这么一道Py无意间,看到这么一道Python面试题:以下代码将输出什么? def testFun(): temp = [lambda x : i*x for i in range(4)] ...

  3. python用哪个版本比较好 2020_2020年最常见的Python面试题答案

    Python新手在谋求一份Python编程工作前,必须熟知Python的基础知识.编程网站DataFlair的技术团队分享了一份2020年最常见Python面试题合集,既有基本的Python面试题,也 ...

  4. python面试题及答案-50道Python面试题集锦(附答案)

    原标题:50道Python面试题集锦(附答案) Python是目前编程领域最受欢迎的语言.在本文中,我将总结Python面试中最常见的50个问题.每道题都提供参考答案,希望能够帮助你在2019年求职面 ...

  5. python面试-2018年最常见的Python面试题答案(上篇)

    Python新手在谋求一份Python编程工作前,必须熟知Python的基础知识.编程网站DataFlair的技术团队分享了一份2018年最常见Python面试题合集,既有基本的Python面试题,也 ...

  6. python面试题及答案-5个很好的Python面试题问题答案及分析

    本文的主要内容是向大家分享几个Python面试中的T题目,同时给出了答案并对其进行分析,具体如下. 本文的原文是5 Great Python Interview Questions,同时谢谢 @非乌龟 ...

  7. 在python中、下列代码的输出是什么-python面试题详细总结(附答案)

    本篇文章给大家带来的内容是关于python面试题总结(附答案),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. CPS少儿编程网-Scratch_Python_教程_免费儿童编程学习平 ...

  8. python fun_一道神奇的Python面试题,你会吗?

    原标题:一道神奇的Python面试题,你会吗? 无意间,看到这么一道Python面试题:以下代码将输出什么? def testFun: temp = [lambda x : i*x for i in ...

  9. python面试题_春招苦短,我用百道Python面试题备战

    对于机器学习算法工程师而言,Python是不可或缺的语言,它的优美与简洁令人无法自拔.那么你了解过Python编程面试题吗?从Python基础到网页爬虫你是否能全方位Hold住?今天,机器之心为读者们 ...

最新文章

  1. 机器学习特征工程之连续变量离散化:连续变量二值化(Binarizer)
  2. 如何让fragment每次都重新加载_每次都能正确判罚丨网球是如何电子化的
  3. python读取xml数据并显示为表格_用Python解析XML数据,然后用SQL创建一个数据库
  4. mysql的性能瓶颈分析、性能指标、性能指标信息的搜集工具与方法、分析调优工具的使用...
  5. python的优点-python的优点
  6. 科大星云诗社动态20201119
  7. cisco数据中心理论小记-3
  8. arcpy 验证中心点是否位于图层之内
  9. (油菜花)为什么别的项目中的category拖到自己项目中无法使用?
  10. inventor牙距_10 INVENTOR螺纹特征,是否有何设置,可让螺纹类型默认为GB Metric
  11. nifi mysql to mysql_NiFi 实战
  12. 计算机基础知识应用电子邮件,计算机基础知识及应用普及
  13. 读《纳瓦尔宝典》感想
  14. Python 类的继承和组合
  15. [ 笔记 ] 计算机网络安全_5_防火墙原理与设计
  16. WAF是什么?一篇文章带你全面了解WAF
  17. 大数据-玩转数据-MaxCompute窗口函数
  18. 计算机考研英语复试问题及回答
  19. 2018年俄罗斯世界杯对阵图(法国冠军杯![2018年 07月 12日 星期四 18:15:26 CST])
  20. 软工网络16个人作业一

热门文章

  1. 在Windows平台下使用安装GCC
  2. OpenLayer + Ags 综合应用(一)--OpenLayer 地图展现
  3. MATLAB实现偏最小二乘回归PLS
  4. python求解方程组
  5. np.c_和np.r_用法
  6. 构造器与一般方法对比
  7. ADO.NET Entity Framework 简介
  8. 从零开始一个http服务器(五)-模拟cgi
  9. bzoj 5092: [Lydsy1711月赛]分割序列
  10. 修改Spring boot内置的tomcat端口