原文地址:https://mp.weixin.qq.com/s/OypPwnJ2vX2vJtZRkVa-Ug

6 月 22 日,Python 之父 Guido 发了一条推特,说了 Python 的一则历史故事,他说 elif 是从 C 语言中偷过来的:

elif 是“else if”的简写,用于条件判断。当只有两个分支时,我们会写成“if...else...”,当出现更多分支时,我们会写成如下格式:

if 判断条件1:做事情1
elif 判断条件2:做事情2
else:做其它事

简写而成的 elif 不仅是减少了几个字符,而且由于单一而清晰的用途,它还不会给我们带来理解或使用上的困惑。

但是,简写法并不是主流,完整写法才是主流,C 语言中就是采用完整的写法:

if(判断条件1)
{做事情1
}
else if(判断条件2)
{做事情2
}
else
{做其它事
}

没错,C 语言使用的是全拼写法,但是在它的预处理/预编译语句中,还有一个 elif 指令,Guido 所说的“偷”,就是从这来的:

#if 常量表达式1
// 编译1
#elif 常量表达式2
// 编译2
#else
// 编译3
#endif

Python 没有预编译,所以所谓的偷,跟预编译没有关系,只是在对比两种写法后,借用了更简洁的写法而已。

为什么 C 语言不把两种写法统一起来呢?这我不得而知了,而 Guido 在两种写法中,选择了后一种非主流却更好用的写法。我想对他说,你“偷”得好啊!

实际上,留言区里的人也有同感,纷纷表示:不介意、很 okay、非常喜欢,还有人说“不是偷,而是收获(harvested)”、“不是偷,而是把它提升了一些高度”……

前不久,我写了一篇《聊聊 print 的前世今生》,print 这个词就是从 C 语言中借用来的。除此之外,如果有人仔细比较这两种语言的关键字和习惯命名,肯定会发现不少相同的内容。

编程语言间有一些共享的元素,这很常见,创造一门语言并不意味着要原创每一个词句,毕竟大部分思想是共通的,作为基础设施的词语更是如此。

那么,我突然好奇了:创造一门编程语言时,什么时候该借用,什么时候该创造呢?

这个问题看起来可能没啥意义,因为终其一生,我们多数人也不大可能会参与创造一门编程语言。

但我觉得它还是极有意义的,首先,提问精神值得肯定,其次,它还提供了一种溯源、甄别、遴选、创造的体系性视角,我认为这是求知的正确思维方式。

带着这个疑惑,我特别想要考察的是 Python 的 for 循环。

如果你有其它语言基础,就知道 “for 循环”通常指的是这样的三段式结构:

for ( init; condition; increment ){statement(s);
}// java
for(int x = 10; x < 20; x = x+1) {System.out.print("value of x : " + x );System.out.print("\n");
}

这种 C 风格的写法是很初级的东西,不少语言都借用了。但是,它的写法实在繁琐,为了更方便地遍历集合中的元素,人们在 for 循环之外又引入了升级版的 foreach 循环:

// java
int[] a = {1,2,3};
for(int i : a){System.out.print(i + ",");
}// C#
int[] a = {1,2,3};
foreach(int i in a){System.Console.WriteLine(i);
}

Python 中也有 for 循环,但是,它借用有度,在设计上早早就有自己独到的考虑,它直接摒弃了三段式的 for 循环,而是采用类似 foreach 的一种写法:

for iterating_var in sequence:statements(s)# 例子
for i in range(3):print(i)for i in "hello":print(i)

从表面上看,Python 的 for 循环跟其它语言的 foreach 很相似,但实际上,它的工作原理却很不相同。

为什么会有不同呢?主要是因为 Python 的 for 语句用于可迭代对象上,而不仅仅是用于集合或者普通的容器(虽然它们也是可迭代对象),而可迭代对象还可再细分出迭代器与生成器,这会造成最终结果的极大差异。

先看看两个例子:

# 例1,普通可迭代对象
x = [1, 2, 3]
for i in x:print(i)
for i in x:print(i)# 例2,迭代器或生成器
y = iter([1, 2, 3])
# y = (i for i in [1,2,3])
for i in y:print(i)
for i in y:print(i)

例 1 中,“1 2 3”会被打印两次,而在例 2 中,则只会打印一次。

普通可迭代对象只有 __iter__() 魔术方法,而不像迭代器一样拥有 __next__() 魔术方法,这意味着它无法实现 自遍历过程,同时在经过 for 循环的 它遍历 后,也不会破坏原有的结构。(这两个是我创造的概念,详见《Python进阶:迭代器与迭代器切片》)。

但是,迭代器是一种匮乏的设计,具有单向损耗的特性,遍历一次后就会被破坏掉,不能重复利用。(关于迭代器的设计问题,这篇文章值得一看《当谈论迭代器时,我谈些什么?》)。

这表明了,Python 中 for 循环的使用场景很广阔,而且它还可能带来非纯结果,即重复执行同样的代码块,会出现不同的结果。

这是不是跟别的语言很不同了呢?相同的关键字,相似的循环思想与写法,但是,带来的影响却有差别。

关于 Python 的 for 循环,还有一个很独特的设计,即 for-else 结构:

x = [1, 2, 3]
for i in x:print(i, end = " ")
else:print("ok")# 输出:1 2 3 ok

本文开头提到了 if-else 结构,只有在不满足 if 条件时,才会执行到 else 部分,也就是说,如果 if 语句为真,那执行完它的语句块后,就会跳过 else 部分。

这是一种非此即彼的并行关系 ,直白地说是“如果...就...;否则就...” 。

但是,对于 for-else 结构,for 语句并不是在做真值判断,它的程序体必然会执行(除非可迭代对象为空),执行后还会继续执行 else 部分。

所以,它是一种先此后彼的串行关系 ,翻译出来则是“对于...就...;然后...”。

这种结构肯定不是从 C 语言中借用来的,至于是否为 Python 所独创,我不确定(大概率是,姑且认为是吧),如果有知情的同学,烦请告知。

那么,为什么 Python 要加上这种设计呢,它有什么实际的用途么?

x = [1,2,3]
for i in x:if i % 2 == 0:print(i)   # matchbreak
else:print("mismatch")

上例的 for 部分增加了一个判断以及 break,这个 break 不仅会跳出 for 循环本身,还会跳过 else 部分。

上例的作用是查找偶数,如果找到则打印出来,如果 for 循环遍历完都找不到,则进入到 else 分支,打印“mismatch”的结果。

所以,其实 else 是 for 循环有没有正常遍历结束的标记,如果在循环后没有达到某种目标而跳出(break、return 或者 raise),就可以在 else 中做必要的补充(记录日志、抛出异常等等)。

这种设计并不算一个好的设计,因为 else 会带来误解(if-else 那种非此即彼的关系),而且它的最大用途需要结合 break 等跳出循环的操作,但是这层信息却非显而易见的。

在核心开发者的邮件列表里,就有不少争论点,2009 年的这封邮件梳理了大家的讨论(https://mail.python.org/pipermail/python-ideas/2009-October/006155.html)。

其中,有开发者提议:

  • 移除这个写法
  • 如果用了却没写 break,就生成告警提示
  • 替换 else 关键字(如 then、finally、else no break)
  • 增加其它的功能

这封邮件一一列举了这些观点的提出原因及改进想法,然后又一一地反驳了它们,最后的结论是保持 for-else 写法不变,也就是大家现在看到的实现方式。它的完整语义是:

execute the for-loop (or while-loop)
if you reach a `break`, jump to the end of the `for...else` block
else execute the `else` suite

也就是说,else 对标的是“是否执行 break”,如果没有 break,则进入else。

但是,我并不认可这种做法,因为 break 是隐含条件,在直观上我们只看到了 for-else,很容易产生 if-else 那样的联想。因此,我反而赞同把 else 改为 then,以消除误会。

这封邮件的反驳意见是,改成 then 会引入新的关键字,因此不好。

我认为这个说法有些牵强(从使用者的角度),还记得本文开头的内容么,elif 就是新引入的关键字啊,看看它现在是多受欢迎。

elif 属于那种初看不知何意,但知道后肯定会记住的词,而且也不大可能拼写错误。为了这点简洁易拼写的好处,它就被引入成新的关键字了。

for-else 中的 else 属于那种初看以为知道含义的词,但实际却表达着不同意思(准确地说是,由于不知道隐含条件,而造成的误解),为了清晰语义的好处,我认为可以引入新的关键词 then 来替代 else。

不过,我转念一想,现在讨论这个已经没有意义了,毕竟时间已经过去了,那都是 10 年前的讨论了。

如果在 Python 创造之初,或者在 Python 3 大版本改动之初,这个讨论就被提出,那很可能 for-else 会被设计成 for-then ,then 会像引入 elif 关键词一样被引入。

如果是那样,说不定 Guido 某天心血来潮说起这则历史小故事,留言区又会出现一大片的赞同之声呢。

聊到这里,意犹未尽,但主题似乎有点跑偏,我们来稍微总结几个要点吧:

  • Python 从 C 中借用了 elif,受到赞许
  • Python 没有借用 C 传统的三段式 for 循环
  • Python 采用类似 foreach 的表达,但应用范围更广
  • Python 的 for 循环由于迭代器的设计原因,会造成一些陷阱
  • Python 创造了 for-else 结构,它的隐含语义是 for-(if break)-else,曾有讨论是否要创造新的关键词替换 for-else,但是被否决了

本文谈到的内容很微小,好像没有什么实际的帮助,不知道 elif 来源、不知道 for 循环的细节、不知道 for-else 的用途与争论,这些统统都不会造成语言使用上的障碍。

但我还是那个观点:

> 阅读 Python 的历史,从中你可以看到设计者们对功能细节的打磨过程,最终你就明白了,Python 是如何一步一步地发展成今天的样子。

这在我看来挺有趣的,更加增进了我对于 Python 的了解,以后在编程到某些用法的时候,脑海里满满都是故事,它顿时也会变得立体生动起来。

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

  1. DNSPod十问陆婷婷:什么是创造101背后的破圈秘诀?

    陆婷婷,兰渡文化创始人兼CEO.毕业于浙江大学新闻学系,曾任美国万通投资控股集团总裁助理,联合创办了美国南加州影响力最大的孵化器Plusyoou并担任营销总监.腾讯.优酷.B站.快手.抖音的官方年度合 ...

  2. 听Ruby之父畅谈编程语言的设计

    本文摘自<松本行弘:编程语言的设计与实现> 1-1 自己创造编程语言的意义 通过实际创造一门新的编程语言,可以学到编程语言的设计思路和实现方法.随着开源的普及,创造新编程语言的门槛一下子降 ...

  3. 计算机考研考编程,计算机考研面试------编程语言

    花了几天是时间整理了下复试的时候编程语言方面可能会问到的问题,以下就是我个人以及网上收集资料整理的关于编程语言可能会问的题目,我已经将这些题目整理到world文档里面去了,点击这里编程语言面试汇总,然 ...

  4. H计算机语言吗,计算机考研面试------编程语言

    花了几天是时间整理了下复试的时候编程语言方面可能会问到的问题,以下就是我个人以及网上收集资料整理的关于编程语言可能会问的题目,我已经将这些题目整理到world文档里面去了,点击这里编程语言面试汇总,然 ...

  5. 软件测试面试题 | 工作5年面试16家,总结大环境下的软件测试岗需求与面试常问

    目录:导读 前言 一.大环境的测试岗 1.20K左右的测试开发岗还是很多的,需要玩懂自动化,自动化是必备选项 2.现在测试真的是越来越卷了,高级测试人员在往全栈的方向发展 3.相比之前年轻的自己,开始 ...

  6. 小众编程语言同样值得你关注

    之前的RubyChina社区上发起了一个讨论:最近有看中什么新的编程语言么?不少网友都列出了自己喜欢的编程语言,在这里,我们搜集了一些大家耳熟能详的小众编程语言,或许在你看来它们根本不是什么小众语言. ...

  7. 985学霸总结的《优雅的Python》火了,完整版开放下载!

    大家好,我是豌豆花下猫,今天很高兴能在涛哥的公众号里作客.接下来我会打一个小小的"广告",希望能引发大家的兴趣,也为大家提供一点点帮助. 我是Python猫公众号的主理人,你可以叫 ...

  8. 小型的编程项目有哪些值得推荐?这本神书写了 22 个,个个了不得

    ? 点击上方蓝字"Python猫",免费获得一个公众号 今天,猫哥要推荐一本非常著名的开源书籍:<500 Lines or Less>. 在开始正题之前,先介绍一下它所 ...

  9. 怎么逐步突破,成为Python高手?

    工作中,我总听到很多程序员抱怨,说现在的计算机编程语言太多了,学不过来了. 一些人 Java 用了很多年,但是最近的项目突然需要用 Python,就会不知所措,压力很大. 众所周知,Facebook ...

最新文章

  1. 2022-2028年中国塑料网格板行业市场行情动态及发展趋向分析报告
  2. excel表格出问题了
  3. 全球及中国电子式儿科呼吸机行业发展现状与市场销量调研报告2022版
  4. 海南工会云会员认证_好消息!海南农民工春节返乡最高有300元补贴!申请条件和流程→...
  5. python精要(66)—类(3)-文档字符串,注释文档
  6. Android 自定义年月日日期选择器、时分时间选择器
  7. 可信计算 沈昌祥_沈昌祥院士:用主动免疫可信计算构筑车联网安全防线
  8. 人是什么垃圾?AI识别功能用到垃圾分类上时 网友试了试“扫自己”
  9. win2008r2 惠普g160鼠标_分享 HP 原机附带 WIN2008 R2 标准版 64位
  10. 彻底抛弃脚本录制,LR脚本之使用web_custom_request函数自定义http请求
  11. C#编程如何安装Nupkg包
  12. 第十八章、ActiveX控件
  13. java云购_ycyg: 源创元购,一元云购java springmvc版本 云购商城 开源一元云购 开源java商城...
  14. C语言怎样求矩阵上三角乘积,C语言经典算法 - 上三角、下三角、对称矩阵
  15. 管家婆设计页面用什么做的_管家婆软件主要是用来做什么的?
  16. HTML实训大作业网页设计:我的简历——响应式个人简历网站模板(1页) HTML+CSS+JavaScript web网页设计制作成品...
  17. 数仓建模—数据资产管理
  18. 计算机图片显示简单原理
  19. 写口算用计算机作文600字,口算比赛作文600字
  20. Prometheus+Grafana (史上最全)

热门文章

  1. deep linux 看视频卡,在Deepin 20等Linux系统下用Chrome看虎牙直播经常卡的处理
  2. ubuntu 20.04双系统安装_win10上跑Ubuntu不用虚拟机不用双系统!
  3. python ddos 检测系统_python 检查是否存在ddos攻击
  4. python抽奖简单小程序游戏_python——(分别用两种方式实现)公司年会抽奖小程序...
  5. 优秀网页案例教你如何排好内容页
  6. 横幅新年促销海报PSD模板,拯救年底节日忙
  7. C4D双十一促销海报模板,参考一下!
  8. 一流设计导航|16map|效率导航最全设计类软件!速度收藏!!
  9. C++虚函数的实现原理(最通俗但并不想专业的解释)
  10. libcareplus应用于Qemu-6.1.0