Python之父:为什么要使用操作符?

Python猫

共 2957字,需浏览 6分钟

 ·

2021-04-20 02:19

△点击上方“Python猫”关注 ,回复“1”领取电子书

 作者:Guido van Rossum
 译者:天天向上@Python程序员
 原文:https://neopythonic.blogspot.com/2019/03/why-operators-are-useful.html

这是我在 python-ideas 上发布的一些东西,但我认为这些很有趣,应该分享给更多的人。

最近有很多关于合并两个 dict 的运算符的讨论。(Python猫注:Guido 指的是 PEP-584 的字典合并操作符,文章写于 2019 年 3月,当时这个 PEP 刚刚诞生,后来已合入 Python 3.9。)

这促使我思考为什么有些人喜欢运算符,我想起了 30 多年前与导师 Lambert Meertens 的一次讨论。

对于数学家来说,运算符对于他们的思考方式至关重要。我们来选取一个简单的操作,比如将两个数相加,并尝试研究它的一些行为。

add(x, y) == add(y, x)         (1)

式(1)表示了加法的交换律。它通常用运算符来书写,这使得它更简洁:

x + y == y + x             (1a)

这似乎是一个小小的收获。

现在我们来考虑一下结合律:

add(x, add(y, z)) == add(add(x, y), z)     (2)

式(2)可以用运算符重写:

x + (y + z) == (x + y) + z     (2a)

这比式(2)容易理解得多,并且我们发现括号是多余的,所以现在我们可以这样写:

x + y + z                      (3)

没有歧义(+ 运算符绑定到左边还是右边并不重要)。

许多其他定律也可以很容易的使用运算符来写。这里还有一个关于加法恒等元素的例子:

add(x, 0) == add(0, x) == x   (4)

相比于

x + 0 == 0 + x == x        (4a)

这里的总的思想就是一旦你学会了这个简单的表示法,用它们写的方程就比用函数表示法写的方程更容易“操作”——就好像我们的大脑用不同的大脑机制来掌握运算符,这是更有效率的方法。

我认为,使用运算符编写的公式更容易被“视觉化”处理就与此有关: 它们利用了大脑的视觉处理机制,而这一机制在很大程度上是在潜意识中运作的,并且它会告诉大脑的意识部分它看到了什么(比如,“椅子”而不是“几块木头连在一起”)。函数符号在我们的大脑中则必须走一条不同的路径,这是无意识的(它与内容的阅读和理解有关,这是在比视觉处理更晚的年龄段才学会/训练的)。

当你将多个运算符结合在一起时,视觉处理的功能就会变得非常明显。例如,考虑一下分配律:

mul (n,add(x, y)) == add(mul (n, x) mul (n, y))     (5)

这写起来很恼火,我相信一开始你是不会看到这个规律的(或者至少你不会立刻看到它,如果我没有提到这是分配律的话)。

与下式比较:

n * (x + y) == n * x + n * y     (5a)

注意,这里也使用了相对的运算符优先级。通常数学家们会把它写得更紧凑:

n(x+y) == nx+ny        (5b)

但是,遗憾的是,目前这超出了 Python 解析器的能力。

运算符表示法的另一个非常强大的方面是,可以方便地将它们应用于不同类型的对象。例如,定律(1)到(5)在x、y和z是相同大小的向量,而n是标量(用0向量代替字面量“0”)时也适用,如果它们是矩阵(同样,n必须是一个标量)也适用。

你可以这样处理不同域中的对象。例如,上面的定律(1)到(5)也适用于函数(n也是一个标量)。

通过明智地选择运算符,数学家们可以运用他们的视觉大脑来帮助他们更好地进行数学研究: 他们会更快地发现新的有趣的定律,因为有时黑板上的符号就会跳到你面前,给你提供一条通往难以捉摸的数学证明的道路。

现在,编程并不完全等同于数学,但我们都知道可读性很重要,这就是 Python 中运算符重载的作用。一旦你内化了运算符具有的简单属性,使用+号进行字符串或列表连接将比纯 OO 表示法更具可读性,上面(2)和(3)解释了(部分程度上)为什么是这样。

当然,这样做绝对有可能做过火——然后你就会使用 Perl。但我认为,那些指出“已经有办法做到这一点”的人忽略了一点,即真正理解这一点是比较容易的:

d = d1 + d2

和下面相比:

d = d1.copy ()

d.update(d2) 

这不仅仅是少了几行代码的事: 第一种形式允许我们使用我们的视觉处理,以帮助我们更快的看到它的意思,并且不会影响我们大脑的其他部分(例如,这些部分可能已经被跟踪 d1 和 d2 的意思占据)。

当然,任何事情都是有代价的。你必须学习运算符,并且在应用于不同对象类型时必须学习它们的属性。(数学中也是如此——对于数字,xy == yx,但是这个属性不适用于函数或矩阵; 另一方面,正如结合律一样, x+y == y+x 适用于所有情况。)

“但是性能呢?”我听见你这样问。好问题。在我看来,可读性第一,性能第二。 在基本的例子(d = d1 + d2)中,与使用 update 的两行代码版本相比,没有性能损失,而且可读性明显提高。

我能想到很多情况,性能差异无关紧要,但可读性是最重要的,对我来说,这是默认的假设(即使在 Dropbox——我们最重要的性能代码已经用丑陋的 Python 或 Go 重写过了)。对于少数性能至关重要的情况,很容易将运算符版本转换为其他版本——一旦你认为这很有必要 (可能是通过分析得出的)。

Python猫技术交流群开放啦!群里既有国内一二线大厂在职员工,也有国内外高校在读学生,既有十多年码龄的编程老鸟,也有中小学刚刚入门的新人,学习氛围良好!想入群的同学,请在公号内回复『交流群』,获取猫哥的微信(谢绝广告党,非诚勿扰!)~


还不过瘾?试试它们




为什么 Python 不用声明类型?

编程语言之问:何时该借用,何时该创造?

为什么面向对象糟透了?

为什么 TCP 会被 UDP 取代?

Python 为什么要有 pass 语句?

为什么range不是迭代器?range到底是什么类型?


如果你觉得本文有帮助
请慷慨分享点赞,感谢啦
浏览 20
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报