52

浅入深谈:一道Python面试题,让我明白了殊途同归,却开始怀疑自己

 5 years ago
source link: http://www.10tiao.com/html/605/201806/2654251615/4.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

点击上方“程序员大咖”,选择“置顶公众号”

关键时刻,第一时间送达!


无意间,看到这么一道Python面试题:以下代码将输出什么?

def testFun():
   temp = [lambda x : i*x for i in range(4)]
   return temp

for everyLambda in testFun():
   print (everyLambda(2))

脑中默默一想,这还用说么,肯定是:

0 
2
4
6

最后一看答案,竟然是:

6 
6
6
6

于是带着怀疑的心态(其实是不服输,不认错),打开编辑器,快速一敲,果然是。

怀疑了人生半天,本来还想黑,WTF Python…然后才想通是自己太生疏......

最后发现原因竟是:Python 的闭包的后期绑定导致的 late binding,这意味着在闭包中的变量是在内部函数被调用的时候被查找。所以结果是,当任何 testFun() 返回的函数被调用,在那时,i 的值是在它被调用时的周围作用域中查找,到那时,无论哪个返回的函数被调用,for 循环都已经完成了,i 最后的值是 3,因此,每个返回的函数 testFun 的值都是 3。因此一个等于 2 的值被传递进以上代码,它们将返回一个值 6 (比如: 3 x 2)。

究竟如何才能实现出这样的结果呢?

想了想,若能立即绑定参数,或者直接不用闭包总该行吧,用另一种方式避免 i 的改写。

回忆了之前所学知识,最后酝酿出了四种解决方案:

第一种:创建一个闭包,通过使用默认参数立即绑定它的参数

def testFun():
   temp = [lambda x ,i=i: i*x for i in range(4)]
   return temp

for everyLambda in testFun():
   print (everyLambda(2))

第二种:使用functools.partial 函数,把函数的某些参数(不管有没有默认值)给固定住(也就是相当于设置默认值)

from functools import partial  
from operator import mul  

def testFun():
   return [partial(mul,i) for i in range(4)]

for everyLambda in testFun():
   print (everyLambda(2))

第三种:优雅的写法,直接用生成器

def testFun():
    return (lambda x ,i=i: i*x for i in range(4))

for everyLambda in testFun():
   print (everyLambda(2))

第四种:利用yield的惰性求值的思想

def testFun():
   for i in range(4):
       yield lambda x : i*x

for everyLambda in testFun():
   print (everyLambda(2))

有了解决方案后,又陷入了怀疑自己,这个题目究竟是考察的是什么?是在考面试者闭包相关知识以及Python 的闭包的后期绑定问题么?

若将题目改成:以下代码输出的结果是(0,2,4,6)么?如果不是,你将会怎么做,让它变成(0,2,4,6)?这样会不会更有意思点呢?欢迎大家出妙招,看究竟有多少招?(哈哈哈!!!)

本文作者简介:一个痴迷于Python语言的业余程序猿,经过半年苦练,经历过从入门到放弃,现在庆幸走到痴迷于Python状态。未来的理想是能够与一群痴迷于Python语言的程序猿做有意义的事。

  • 作者:菜鸟分析

  • 原文链接:https://zhuanlan.zhihu.com/p/33376761

  • 程序员大咖整理发布,转载请联系作者获得授权

【点击成为Java大神】


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK