共计 4050 个字符,预计需要花费 11 分钟才能阅读完成。
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 这个词就是从 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 中 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) # match
break
else:
print("mismatch")
上例的 for 局部减少了一个判断以及 break,这个 break 不仅会跳出 for 循环自身,还会跳过 else 局部。
上例的作用是查找偶数,如果找到则打印进去,如果 for 循环遍历完都找不到,则进入到 else 分支,打印“mismatch”的后果。
所以,其实 else 是 for 循环有没有失常遍历完结的标记,如果在循环后没有达到某种指标而跳出(break、return 或者 raise),就能够在 else 中做必要的补充(记录日志、抛出异样等等)。
这种设计并不算一个好的设计,因为 else 会带来误会(if-else 那种非此即彼的关系),而且它的最大用处须要联合 break 等跳出循环的操作,然而这层信息却非不言而喻的。
在外围开发者的邮件列表里,就有不少争论点,其中,有开发者提议:
- 移除这个写法
- 如果用了却没写 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 常识欢送返回公众号:Python 编程学习圈 ,发送“J”即可收费获取,每日干货分享