Python 之父为什么嫌弃 lambda 匿名函数?
点击上方“小白学视觉”,选择加"星标"或“置顶”
重磅干货,第一时间送达
本文转自:AI算法与图像处理
Python 支持 lambda 匿名函数,其扩展的 BNF 表示法是lambda_expr ::= "lambda" [parameter_list] ":" expression
,也就是lambda 参数序列:表达式
。
这是一种便捷的函数定义方式,若翻译成我们熟知的函数形式,会是这个样子:
def <lambda>(parameter_list):return expression
也就是说,Python 中的 lambda 函数是一种可接收多个参数的函数,返回值是一个表达式。
它最大的好处是单行简洁,不需要函数命名与换行缩进。
不得不说,匿名函数有时候是挺好用的,比如下文会介绍到的一些常见用法,它因此受到了不少人的推崇。
但是,匿名函数通常也会造成代码难以阅读,容易被人滥用,再加上 Python 只提供了对它的“残疾的”支持,所以又有一些观点不建议使用匿名函数。
事实上,Python 之父 Guido van Rossum 就属于“不推荐使用派”,他甚至曾经(2005年)想要移除 lambda,只不过最后妥协了。
出处:https://www.artima.com/weblogs/viewpost.jsp?thread=98196
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(lambda i:i+1, my_list)) # 过滤小于10的元素,结果:[3, 1, 5, 4]
list(filter(lambda i:i<10, my_list)) # 元素累加,结果:33
from functools import reduce
reduce(lambda i,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=lambda item:item[1])
初学者也许会觉得代码读不懂,但是只要记住“Python中的函数是一等公民”,知道一个函数可以被作为另一个函数的参数或者返回值,就容易理解了。
比如对于 map() 函数的例子,你可以理解成这个形式:
my_func = lambda i:i+1
list(map(my_func, my_list))
甚至可以还原成普通的函数:
def add_one(i):return i+1list(map(add_one, my_list))
map() 函数的第一个参数是一个函数,第二个参数是一个可迭代对象。这第一个参数会迭代地调用第二个参数中的元素,调用的结果以迭代器的形式返回。
这个例子使用了 list(),是为了方便一次性取出迭代器中的元素,直观地展示出来,在实际使用中,很可能会是基于迭代器的形式。
由这几种用法,我们可以总结出 lambda 函数的使用规律:
它出现在需要使用函数的地方
它适合实现简单的功能
它是一次性的用途,不能在其它地方复用
它一般不会被独立使用,总是作为其它函数的一部分
2、lambda 有什么问题?
由上面的用法可以看出,使用 lambda 函数的代码比较紧凑简洁,所以有人称它体现了“Pythonic”的优雅思想。
但是,lambda 函数有没有什么缺陷呢?
有!当前的 lambda 函数有一个最大的问题,即只支持单行表达式,无法实现丰富的功能,例如无法在函数创建时使用语句(statement),无法使用 if-else 的判断条件,也无法使用 try-except 的异常捕获机制,等等。
这极大地限制了它的能力,导致了它被人诟病为“残疾的”。
从技术实现的角度上看, 这个问题可以通过语法层面的设计来解决。
在当年的邮件组讨论中,有人提出过一些解决思路,比如这封邮件:
出处:https://mail.python.org/pipermail/python-dev/2006-February/060654.html
它提出了一个lambda args::suite
的想法,支持写成这样的形式:
ss = sorted(seq, key=(lambda x::try: return abs(x)except TypeError: return 0))
但是,Guido 很快就否决了这个思路。
他写了一篇文章《Language Design Is Not Just Solving Puzzles》来回应:
出处:https://www.artima.com/weblogs/viewpost.jsp?thread=147358
其基本观点是:不能光顾着解决一个问题/实现某种功能,就引入缺乏“Pythonicity”的语言设计。
那么,为什么 Guido 会认为这是一种不好的设计呢?
我试着概括一下,理由是:
双冒号“::”凭空在此引入,但是跟切片语法中的“::”完全不同,而且跟 C++/Perl 中的作用域操作符用法也不同
即使不用双冒号,用其它符号表示(比如单冒号),还是难以接受,因为都会在一个表达式中嵌入缩进代码块。这就跟使用花括号和 begin/end 关键字来作语句分组(statement grouping)一样,都令人难以接受
在 lambda 中实现其它功能并不重要,这还会让解析器变得复杂(需区分是否有缩进、记录缩进级别),显得小题大做了
简而言之,他认为简洁友好的用户体验更为重要,如果简洁的语法无法满足需求,就应该写成具名函数的形式,而非设计出复杂的匿名函数。
3、为什么 Guido 想移除 lambda?
上文提到的多行 lambda 语句(multi-statement lambda)事件发生在 2006 年,我们看到了 Guido 不想给 lambda 引入复杂设计的原因。
但是,早在 2005 年,Guido 就曾经想要从 Python 移除 lambda,他对它的“嫌弃”是一个“历史悠久”的传统……
在《The fate of reduce() in Python 3000》这篇短文中,Guido 提出要一次性移除 reduce()、map()、filter() 以及 lambda。
移除 lambda 的理由如下:
对于不熟悉 Lisp 或 Scheme 的用户,lambda 这名字容易造成混淆
很多人误以为匿名函数能做嵌套函数不能做的事,但其实并无区别;存在lambda,就会造成不必要的选择,减少选择,可以简化思维
移除 reduce()、map() 和 filter() 后,就没必要写简短的局部函数了
回顾一下我们在前文中总结出的 lambda 的 4 条使用规律,可以发现它跟几个高阶函数(可以接收其它函数作为参数的函数)有较强的“寄生关系”,如果它们能移除了的话,lambda 确实就没有什么独立存留的意义了。
那么,为什么 Guido 觉得应该移除那几个高阶函数呢?
主要的理由有:
可以替换成更加清晰的列表解析式或者生成器表达式,例如 filter(P,S) 可以写成 [x for x in S if P(x)],map(F, S) 写成 [F(x) for x in S]
至于 reduce(),他说这是最讨厌的,除了涉及 + 和 * 的少数用法,其它时候他总要拿出纸笔来画图解才能搞清楚。除了显式地写循环,他还针对 reduce() 的几种用法而提出了几个替代用法,包括引入新的 any() 和 all() 函数
总体而言,Guido 的想法暗合了《The Zen of Python》中的这一条:There should be one-- and preferably only one --obvious way to do it。
但是回到现实,为了照顾某些人的习惯,以及对兼容性的考虑,Guido 最后保守地放弃了“清理异端”的计划。
因此,lambda 得以从 Python 最高独裁者的手上死里逃生。直到一年后,它试图兴风作浪(多行表达式),却惨遭镇压。
我仿佛听到了 Guido 的内心 OS:当初我想删除东西的时候,你们百般阻挠,现在你们想添加东西,哼,没门!……
哈哈,开了个玩笑。
Guido 的所有决定都体现了他的 Pythonic 设计美学、自恰的逻辑一致性以及对社区声音的权衡。
对于 lambda,我认可他的观点,而通过回溯语法发展的历史,我觉得自己对于 Python 的理解变得更为丰富了。不知道你可有同感?
下载1:OpenCV-Contrib扩展模块中文版教程
在「小白学视觉」公众号后台回复:扩展模块中文教程,即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。
下载2:Python视觉实战项目52讲
在「小白学视觉」公众号后台回复:Python视觉实战项目,即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。
下载3:OpenCV实战项目20讲
在「小白学视觉」公众号后台回复:OpenCV实战项目20讲,即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。
交流群
欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~
Python 之父为什么嫌弃 lambda 匿名函数?相关推荐
- Python之高阶函数(abs、map、reduce、filter、lambda匿名函数)
Python之高阶函数(abs.map.reduce.filter.lambda匿名函数) 什么是内置高阶函数 高阶函数:一个函数可以作为参数传给另外一个函数,或者一个函数的返回值为另外一个函数(若返 ...
- C++11 lambda匿名函数看这一篇足以
文章目录 1.lambda匿名函数的定义 2.lambda匿名函数中的[外部变量] lambda 源自希腊字母表中第 11 位的 λ,在计算机科学领域,它则是被用来表示一种匿名函数.所谓匿名函数,简单 ...
- java 匿名函数_Java中的lambda匿名函数使用
Java中的lambda匿名函数使用 lambda匿名函数的使用是为了满足某些情况下需要临时定义函数,或者事先定义,需要时才使用.在python里面,lambda表达式的表达方式为:lambda 参数 ...
- lambda匿名函数
lambda匿名函数 1. lambda是匿名函数,但是可以命名,名字在等号左边 2. lambda中只能包含一个表达式,不能包含复合语句 语法: <函数名> = lambda <参 ...
- 自定义函数变量的设置(*/**),lambda匿名函数(map/filter/zip/enumerate)
自定义函数变量的设置 一个星号 *传入元组,且这个变量为可变变量 两个星号 **后的那些参数被当做字典类型传递到函数中 带有双星号的可变参数只能出现在参数列表的最后 注意双星号是字典类型(字典形式要有 ...
- 内置函数与lambda匿名函数
内置函数 all(iteralble) 如果可迭代对象内的所有元素的bool值为真,那么返回真,如果这个可迭代对象是空,也返回真. all([1,2,3,4]) # return True all([ ...
- A19-Python基础之lambda匿名函数-过滤器-映射
1.lambda匿名函数 1.1 示例: def ds(x):return 2*x+1 ds(5) #11 匿名函数语法: lambda x,y:2*x+y :前为参数,:后边为运算式 g = lam ...
- 列表排序方法sort()的key参数取值,以及lambda匿名函数
1.sort()方法的key参数 li = [[1, 7], [1, 5], [2, 4], [1, 1]] li.sort() print(li) #[[1, 1], [1, 5], [1, 7], ...
- python lambda函数_python入门基础之lambda匿名函数
lambda的一般形式是关键字lambda后面跟一个或多个参数,紧跟一个冒号,以后是一个表达式.lambda是一个表达式而不是一个语句.它能够出现在Python语法不允许def出现的地方.作为表达式, ...
最新文章
- python 读取excel 表格的数据
- php while 存钱,php趣味编程 -php存钱的问题
- 个推数据统计产品(个数)iOS集成实践
- VS2008下利用#pragma comment屏蔽VC/VS生成的exe程序的CMD窗口
- [JAVA基础类库] String类 ○ StringBuffer类 ○ StringBuilder类
- [LeetCode]题解(python):076-Minimum Window Substring
- php foreach id是否存在数组_请纠正这 5 个 PHP 编码小陋习
- Softmax loss, softmax, multinominal and logistic loss
- 信息学竞赛的常数优化、常见问题、代码风格相关
- MATLAB GUI如何创建Callback函数
- ExtJs radiogroup form.loadRecord方法无法赋值正确解决办法
- springMVC接收数据和响应返回
- This is Huge! PhysBAM code is going to be released?
- 关于直播,所有的技术细节都在这里了(3)《转载》
- Vue自定义插件方法大全
- 16:忽略大小写的字符串比较
- element tabs label 插槽
- linux中的clock shew 问题的解决方法
- 基于深度学习的红外和可见光图像融合论文及代码整理
- win7自定义随机更换显示桌面背景图片
热门文章
- 云计算赋能人工智能,未来的红利在哪?
- 苹果Siri团队被合并,由谷歌前高管直接领导
- 专访 | 商汤HPC负责人刘文志(风辰):未来战略的两大方向及招人的4个标准
- 为什么大多数IOC容器使用ApplicationContext,而不用BeanFactory
- 用 float 存储金额,老板说损失从工资里扣!
- 请不要将抛出异常作为业务逻辑使用!!!
- Spring Cloud Alibaba教程:使用Nacos作为服务注册中心
- 我在交大的数学建模经验!
- 10年以后,Google Labs再次回归!VR部门负责人任新leader
- 博士买房后发现被坑,于是写万字论文维权,网友:维权界的天花板...