内置函数是Python的一大特色,很多常用的操作都是用最少的实现句法。它们是预先定义在内置命名空间中的,开箱即用,所见??即所得。Python是公认的对新手友好的语言,这一说法可以成立,内置函数在其中起着关键作用。比如求一个字符串x的长度,Python的写法是len(x),这种写法同样适用于列表、元组、字典等对象,只需要传入相应的参数。len()函数是共享的。这是极简主义哲学的体现:简单胜于复杂。但是,在某些语言中情况并非如此。比如在Java中,string类有找长度的方法,其他类也有自己的找长度方法,不能共享。每次使用时,都是通过类或实例调用的。同样是求字符串的长度,Python的写法:saying="Helloworld!"print(len(saying))#结果:12在Java中,写法可能如下(为简单起见):Stringsaying="Helloworld!";System.out.println(saying.length());//结果:12Python使用前缀表达式,而Java使用后缀表达式。除了找长度,Python的一些内置函数也可以在Java中找到对应的表达式。例如,要将数字字符串s转换为整数,Python可以使用int(s)函数,而Java可以使用Integer.parseInt(s);将整数转换为字符串,Python可以使用str(i),Java也有String.valueOf(i)。Python的内置函数不绑定到特定的类,它们是一流的对象。Java的“函数”不能没有类而存在,它们只是附属物。从直观的角度来看,Python的表达方式似乎更加优化。然而,它们没有可比性,因为这是两个语言系统,每个都有独特的分类背景,不能轻易减少。例如,你不能因为拉丁字母的笔画简单就说它优于汉字,因为字母(注音字符)在表达意思时远不如汉字(表意字符)。同样,日文借用汉字部首而造的字,虽然比较省墨,但却完全失去了意义。在这个类比中,Python的内置函数虽然有方便之美,但是却失去了一些表达功能。有些人在质疑/批评Python时,也喜欢借用这一点,认为这是Python的设计缺陷。这就引出了本文最想讨论的问题:为什么Python要设计成len(x)这样的前缀表达式,而不是x.len()这样的后缀表达式?其实后缀设计也是可行的。以Python中list的两种方法为例:mylist=[2,1,3,5,4]mylist.sort()print(mylist)#[1,2,3,4,5]mylist.reverse()print(mylist)#[5,4,3,2,1]都是通过list对象调用的,并不是凭空从内置命名空间中取出来的。语义表达也很明确,就是对mylist进行排序和反转。很巧的是,他们还有两个同父异母的兄弟sorted()和reversed(),都是前缀表达式。mylist=[2,1,3,5,4]sort_list=sorted(mylist)print(sort_list)#[1,2,3,4,5]reverse_list=reversed(mylist)print(list(reverse_list))#[4,5,3,1,2]的写法不同,但都是做同样的事情(不管它们的副作用如何)。所以,后缀语法也不是不可以,之所以不用,肯定是刻意设计的。回到之前的问题:为什么是len(x)而不是x.len(x),这源于Python的什么设计思想?Python之父GuidovanRossum曾经解释过这个问题(见文末链接),原因有二:对于某些操作,前缀比后缀更易读——前缀(和中缀)表示法历史悠久在数学史上,其视觉效果帮助数学家思考问题。我们可以简单地将公式x*(a+b)重写为x*a+x*b,但是以本机面向对象的方式做同样的事情会很笨拙。当我读到len(x)时,我知道这是一个对象的长度。它告诉我两件事:返回值是一个整数,参数是某种容器。但是在读取x.len()时,我必须提前知道实现了接口的某种容器x,或者继承了一个具有标准len()方法的类。我们经常目睹这种混淆:一个没有实现映射接口但具有get()或keys()方法的类,或者某个非文件对象,但具有write()方法。在解释完这两个原因后,Guido也总结了一句话:“我把‘len’看作是一个内置操作”。这不仅是说len()更具可读性和易懂性,而且是在彻底提升len()的地位。这就好比说分数?中的横线是数学中的“内置”表达式,不需要实现任何接口之类的。已经表达了“某数除某数”的意思了。不同类型的数(整数、浮点数、有理数、无理数……)共用同一个运算符,不需要对每种数据都实现小数运算。优雅易懂是Python追求的设计理念,len()函数的前缀表达式就是最好的体现。记得在《超强汇总:学习Python列表,只需这篇文章就够了》一文中引用了Guido对“为什么索引从0开始”的解释。最重要的原因是从0开始的索引是最优雅和最容易理解的。我们先看看切片的使用。也许最常见的用法是“取前n个元素”或“从第i个索引开始,取最后n个元素”(前一种用法实际上是i==起始位的特殊用法)。如果这两种用法可以在表达式中没有丑陋的+1或-1的情况下实现,那将是非常优雅的。使用基于0的索引、半开区间切片、默认匹配区间(Python最终采用了这种方式),以上两种情况的切片语法变得非常漂亮:a[:n]和a[i:i+n],前者是a[0:n]的缩写。因此,我们可以说len(x)胜??过x.len(),支撑它的是一个简单、纯粹但深刻的设计思想。面向对象的编程语言,自从它们被发明以来,就想模拟我们生活的真实世界。但是什么样的类,接口,对象,以及它们的方法,这些东西的毒有时会蒙蔽我们看清世界的本质。桌子有求桌子长度的方法,椅子有求椅子长度的方法,层出不穷,但真的是这样吗?求长度的方法不能是一个独立的对象吗?它存在是因为有“对象”,而不是因为有某个类。所以,我会说len(x)胜??过x.len(),这也反映了Python对世界本质的洞察力。求一个对象的长度,这个操作独立于对象存在,不属于对象内部的属性或函数。从这个角度,我们就可以理解Python为什么要设计内置函数了?内置函数实际上是对世界本质的捕捉。这些微小的发现足以让我们爱上这门语言。人生苦短,我用Python。相关阅读:Guido解释len的由来:http://suo.im/4ImAEoGuido解释0索引的由来:http://suo.im/5cr12S请勿转载。原文地址:https://mp.weixin.qq.com/s/pK...公众号【Python猫】,本号连载系列精品文章,包括喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐及翻译等,欢迎关注。后台回复“爱学习”,免费领取学习大礼包。
