当前位置: 首页 > 后端技术 > Python

为什么python之父不喜欢lambda匿名函数?_0

时间:2023-03-26 12:19:26 Python

作者:猫下豌豆花来源:Python猫python支持lambda匿名函数,其扩展的BNF表示法为lambda_expr::="lambda"[parameter_list]":"expression,即lambda参数序列:表达式。这是定义函数的便捷方式。如果翻译成大家熟知的函数形式,会是这样:def(parameter_list):returnexpression也就是说,python中的lambda函数是一个可以接收多个参数的函数,返回值是一个表达式。它最大的优点是单行简洁,不需要函数命名和换行缩进。不得不说,匿名函数有时候是非常有用的,比如下面会介绍的一些常用用法,所以受到很多人的推崇。但是,匿名函数通常会使代码难以阅读并且容易被滥用。另外,Python只对它提供了“禁用”的支持,所以有些意见不建议使用匿名函数。事实上,Python之父GuidovanRossum就属于“弃用派”。他甚至有一次(2005年)想去掉lambda,但最后妥协了。Lambda,一个由其他开发者贡献的特性(借鉴自lisp语言),已经存在了十多年,却被语言的创造者(兼首席设计师)拒绝,最后奇迹般地活了下来接下来,你觉得这个故事怎么样是不是很戏剧化?接下来我们就来说说本文中这个尴尬但顽强的lambda匿名函数吧!1.如何使用lambda?Lambda函数通常与map()、reduce()、filter()、sorted()等函数结合使用,这些函数的共同点是都可以接收其他函数作为参数。例如下面的例子:my_list=[3,1,5,4,10]所有元素加1,结果:[4,2,6,5,11]list(map(lambdai:i+1,my_list))过滤小于10的元素,结果:[3,1,5,4]list(filter(lambdai:i<10,my_list))元素累加,结果:33fromfunctoolsimportreducereduce(lambdai,j:i+j,my_list,10)按值排序的字典,结果:[('b',1),('a',3),('d',4),('c',5)]my_dict={'a':3,'b':1,'c':5,'d':4}sorted(my_dict.items(),key=lambdaitem:item[1])初学者可能会觉得代码难读,但是只要记住“Python中的函数是一等公民”,知道一个函数可以作为另一个函数的参数或返回值就很容易理解了。比如map()函数的例子,你可以理解为这样的形式:my_func=lambdai:i+1list(map(my_func,my_list))甚至可以简化为一个普通的函数:defadd_one(i):returni+1list(map(add_one,my_list))map()函数的第一个参数是一个函数,第二个参数是一个可迭代对象。第一个参数会迭代调用第二个参数中的元素,调用结果以迭代器的形式返回。本例使用list()是为了方便一次性取出迭代器中的元素并直观显示。在实际使用中,很可能是基于迭代器的。从这些用法中,我们可以总结出lambda函数的使用规则:出现在函数需要使用的地方,适合简单的函数,一次性使用,不能在其他地方重复使用。它一般不单独使用,总是作为其他函数的一部分2.lambda有什么问题?从上面的用法可以看出,使用lambda函数的代码比较紧凑简洁,所以有人说它体现了“Pythonic”的优雅思想。但是lambda函数有什么缺点吗?有!目前lambda函数最大的问题是只支持单行表达式,无法实现丰富的功能。例如,它在创建函数时不能使用语句(statements),不能使用if-else判断条件,不能使用try-except异常捕获机制,等等。这极大地限制了它的能力,导致它被称为“残疾人”。从技术实现的角度来看,这个问题可以通过语法层面的设计来解决。当年的邮件群讨论中,有人提出了一些解决方案,比如这个邮件:来源:https://mail.python.org/piper...提出了一个lambdaargs::suite的idea,支持写成这样的形式:ss=sorted(seq,key=(lambdax::try:returnabs(x)exceptTypeError:return0))然而,Guido很快否定了这个想法。他写了一篇文章《Language Design Is Not Just Solving Puzzles》作为回应:来源:https://www.artima.com/weblog...其基本观点是:不能光顾解决一个问题/实现某个功能,只介绍不足“Pythonicity”语言设计。那么为什么Guido认为这是一个糟糕的设计呢?我尝试归纳一下,原因是:这里凭空引入了双冒号“::”,但它在切片语法中与“::”完全不同,而且在用法上也与作用域运算符不同C++/Perl即使没有双冒号。由其他符号(例如单冒号)表示的冒号仍然是不可接受的,因为它们都在表达式中嵌入了缩进的代码块。这与使用花括号和begin/end关键字进行语句分组(statementgrouping)是一样的,这是不可接受的。在lambda中实现其他功能并不重要,这也使解析器变得复杂(需要区分是否缩进,记录缩进级别),似乎有点大材小用。总之,他认为简单友好的用户体验更为重要。如果简洁的语法不能满足需要,就应该写成命名函数的形式,而不是设计一个复杂的匿名函数。3.为什么Guido要去掉lambda?上面提到的多语句lambda事件发生在2006年,我们看到了Guido为什么不想在lambda中引入复杂的设计。然而,早在2005年,Guido就想将lambda从Python中移除,而他对它的“厌恶”是“由来已久”的传统……在这篇短文《The fate of reduce() in Python 3000》中,Guido提出了一次性移除lambda。除了reduce()、map()、filter()和lambda。删除lambda的原因如下:对于不熟悉Lisp或Scheme的用户,lambda这个名字很容易混淆。很多人误以为匿名函数可以做嵌套函数做不到的事情,其实没有区别;不必要的选择,减少选择可以简化思维。去掉reduce()、map()和filter()之后,就不需要再写短小的局部函数了。回顾上一篇我们总结的lambda的4条使用规则,可以发现它与几个高阶函数(可以接收其他函数作为参数的函数)有着很强的“寄生关系”。如果能去掉它们,lambda就没有任何独立存在的意义了。那么,为什么Guido认为应该删除那几个高阶函数呢?主要原因是可以用更清晰的列表理解或生成器表达式代替,例如filter(P,S)可以写成[xforxinSifP(x)],map(F,S)可以写成[F(x)forxinS]至于reduce(),他说这个最烦人,除了少数+和*的用法,其他时候总是要拿出笔来和纸来画图来弄清楚。除了显式编写循环之外,他还提出了reduce()的几种替代用途,包括引入新的any()和all()函数。总的来说,Guido的想法与《The Zen of Python》不谋而合:应该有一种——最好只有一种——显而易见的方法来做到这一点。不过回到现实,出于照顾一些人的习惯和兼容性的考虑,圭多最终还是保守地放弃了“清理异端”的计划。因此,lambdas险些逃脱了Python最高独裁者的控制。直到一年后,它试图兴风作浪(多行表达),却遭到残酷镇压。我仿佛听到了Guido内心的OS:我想删东西的时候,你百般阻挠,现在你又想加东西,哼,不行!……哈哈,开玩笑的。Guido的所有决定都反映了他的Pythonic设计美学、自洽的逻辑一致性以及对社区声音的权衡。对于lambda,我很认同他的观点,并且通过回溯语法发展的历史,感觉对Python的理解更加丰富了。不知道你有没有同感?