共计 5901 个字符,预计需要花费 15 分钟才能阅读完成。
0、前言
我在上一篇文章中剖析了 为什么 Python 没有 void 类型 的话题,在文章公布后,有读者跟我探讨起了另一个对于类型的问题,然而,咱们很快就呈现了重大一致。
咱们次要的一致就在于:Python 到底是不是强类型语言? 我认为是,而他认为不是。
他写了一篇很长的文章《谁通知的你们 Python 是强类型语言!站进去,保障不打你!》,专门重申了他的观点,但惋惜错漏百出。
我曾有想法要写写对于 Python 类型的问题,当初借着这个机会,就来系统地梳理一下吧。
(PS:在我写作进行到差不多一半的时候,微信读者群里恰好也探讨到“强弱类型”的话题!在与大家探讨时,我的一些想法失去了验证,同时我也学到了很多新常识,所以本文的局部内容有群友们的功绩,特此鸣谢!)
1、动静类型与强弱类型
很多读者应该都相熟 动静类型
与 动态类型
,然而很多人也会把它们跟 强弱类型
一概而论,所以咱们有必要先作一下概念上的廓清。
这两组类型都是针对于编程语言而言的,但关注的外围问题不同。
对于“动静类型”概念,它的外围问题是“什么时候晓得一个变量是哪种类型”?
一般而言,在编译期就确定变量类型的是动态类型语言,在运行期才确定变量类型的则是动静类型语言。
例如,某些语言中定义函数“int func(int a){…}”,在编译时就能确定晓得它的参数和返回值是 int 类型,所以是动态类型;而典型如 Python,定义函数时写“def func(a):…”,并不知道参数和返回值的类型,只有到运行时调用函数,才最终确定参数和返回值的类型,所以是动静类型
对于“强弱类型”概念,它的外围问题是“不同类型的变量是否容许隐式转化”?
一般而言,编译器有很少(正当)隐式类型转化的是强类型语言,有较多(过分)隐式类型转化的是弱类型语言。
例如,Javascript 中的 "1000"+1
会失去字符串“10001”,而 "1000"-1
则会失去数字 999,也就是说,编译器依据应用场合,对两种不同类型的对象别离做了隐式的类型转化,然而类似的写法,在强类型语言中则会报类型出错。(数字与字符串的转化属于过分的转化,下文会再提到一些正当的转化。)
依照以上的定义,有人将常见的编程语言画了一张分类图:
按强弱类型维度的划分,能够演绎出:
- 强类型:Java、C#、Python、Ruby、Erlang(再加 GO、Rust)……
- 弱类型:C、C++、Javascript、Perl、PHP、VB……
2、过来的强弱类型概念
动静类型的概念基本上被大家所认可,然而,强弱类型的概念在问答社区、技术论坛和学术讨论上却有很多的争议。此处就不作列举了。
为什么会有那么多争议呢?
最次要的起因之一是有人把它与动静类型混用了。
最显著的一个例子就是 Guido van Rossum 在 2003 年加入的一个访谈,它的话题恰好是对于强弱类型的(Strong versus Weak Typing):
然而,他们议论的显著只是动静类型的区别。
访谈中还引述了 Java 之父 James Gosling 的话,从他的表述中也能看出,他说的“强弱类型”其实也是动静类型的辨别。
另外还有一个经典的例子,C 语言之父 Dennis Ritchie 已经说 C 语言是一种 “强类型然而弱查看” 的语言。如果对照成前文的定义,那他其实指的是“动态类型弱类型”。
为什么这些大佬们会有混同呢?
其实起因也很简略,那就是在过后还没有明确的动静类型与强弱类型的概念之分!或者说,那时候的强弱类型指的就是动静类型。
维基百科上给出了 1970 年代对强类型的定义,根本能够还原成前文提到的动态类型:
In 1974, Liskov and Zilles defined a strongly-typed language as one in which “whenever an object is passed from a calling function to a called function, its type must be compatible with the type declared in the called function.”[3] In 1977, Jackson wrote, “In a strongly typed language each data area will have a distinct type and each process will state its communication requirements in terms of these types.”[4]
后面几位编程语言之父应该就是持有相似的观点。
不过,大佬们也意识到了过后的“强弱类型”概念并不充沛精确,所以 Dennis Ritchie 才会说成 “强类型然而弱查看”, 而且在访谈中,Guido 也特别强调了 Python 不应该被称为弱类型,而应该说是 运行时类型(runtime typing)。
然而在那个晚期年代,基本上强弱类型就等同于动静类型,而这样的想法至今仍在影响着很多人。
3、当初的强弱类型概念
晚期对于编程语言的分类其实是混淆了动静与强弱两个维度,然而,它们并不是一一对应重合的关系,并不足以表白编程语言间的区别,因而就须要有更为明确 / 丰盛的定义。
有人提出了“type safety”、“memory safety”等辨别维度,也呈现了动态查看类型和动静查看类型,与强弱类型存在肯定的交加。
直到呈现 2004 年的一篇集大成的学术论文《Type Systems》(出自微软研究院,作者 Luca Cardelli),专门钻研编程语言的不同类型零碎:
论文中对于强弱查看(也即强弱类型)有一个简短的归纳如下:
- Strongly checked language: A language where no forbidden errors can occur at run time (depending on the definition of forbidden error).
- Weakly checked language: A language that is statically checked but provides no clear guarantee of absence of execution errors.
其要害则是程序对于 untrapped errors 的查看强度,在某些理论已出错的中央,弱类型程序并不作捕捉解决,例如 C 语言的一些指针计算和转换,而《C 程序员十诫》的前几个都是弱类型导致的问题。
论文对于这些概念的定义还是比拟形象的,因为未捕捉的谬误(untrapped errors)大多是因为隐式类型转换所致,所以又演化出了第一节中的定义,以隐式类型转换作为判断规范。
现在将 “对隐式类型转换的容忍度” 作为强弱类型的分类规范,曾经是很多人的共识(尽管不够全面,而且有一些不同的声音)。
例如,维基百科就把隐式类型转换作为弱类型的次要特点之一:
A weakly typed language has looser typing rules and may produce unpredictable results or may perform implicit type conversion at runtime.
例如,以 Python 为例,社区的支流认识认为它是强类型语言,而判断的规范也是看隐式类型转换。
例子有很多,比方 Python 官网的 wiki,它专门答复了 Why is Python a dynamic language and also a strongly typed language,给出了 4 个答案,为 Python 的“动静强类型”定性:
再比方,在《晦涩的 Python》第 11 章的杂谈中,也专门提到了强弱类型的分类。(它的用语是“很少隐式类型转换”,算是比拟谨严的,然而也谬误地把 C++ 归为了强类型。)
4、Python 是不是强类型语言?
对于“Python 是否属于强类型”话题,在支流观点之外,还存在着不少误会的认识。
一方面的起因有些人混用了强弱类型与动静类型,这有历史的起因,后面曾经剖析了。
另外还有一个同样重要的起因,即有人把弱类型等同于“齐全没有隐式类型转换”了,这种想法并不对。
事实上,强弱类型的概念中蕴含着局部相对主义的含意,强类型语言中也可能有隐式类型转换。
比方,Rust 语言为了实现“内存平安”的设计哲学,设计了很弱小的类型零碎,然而它外面也有隐式类型转换(主动解援用)。
问题在于:怎么样的隐式类型转换是在正当范畴内的?以及,某些外表的隐式类型转换,是否真的是隐式类型转换?
回到 Python 的例子,咱们能够剖析几种典型的用法。
比方,"test"*3
这种字符串“乘法”运算,尽管是两种类型的操作,然而并不波及隐式类型转换转化。
比方,x=10; x="test"
先后给一个变量不同类型的赋值,外表上看 x 的类型变动了,用 type(x) 能够判断出不同,然而,Python 中的类型是跟值绑定的(右值绑定),并不是跟变量绑定的。
变量 x 精确地说只是变量名,是绑定到理论变量上的一个标签,它没有类型。type(x) 判断出的并不是 x 自身的类型,而是 x 指向的对象的类型,就像内置函数 id(x) 算出的也不是 x 自身的地址,而是理论的对象的地址。
比方,1 + True
这种数字与布尔类型的加法运算,也没有产生隐式类型转换。因为 Python 中的布尔类型其实是整型的子类,是同一种类型!(如果有疑难,可查阅 PEP-285)
再比方,整数 / 布尔值与浮点数相加,在 Python 中也不须要作显式类型转换。然而,它的实现过程其实是用了数字的__add__()
办法,Python 中所有皆对象,数字对象也有本人的办法。(其它语言可不肯定)
也就是说,数字间的算术运算操作,其实是一个函数调用的过程,跟其它语言中的算术运算有着实质的区别。
另外,不同的数字类型尽管在计算机存储层面有很大差别,但在人类眼中,它们是同一种类型(宽泛地分),所以就算产生了隐式类型转换,在逻辑上也是能够承受的。
最初,还有一个例子,即 Python 在 if/while 之后的真值判断,我之前剖析过它的实现原理,它会把其它类型的对象转化成布尔类型的值。
然而,它实际上也只是函数调用的后果(__bool__() 和 __len__()),是通过计算而得出的正当后果,并不属于隐式的强制类型转换,不在 untrapped errors 的领域里。
所以,严格来说,后面 5 个例子中都没有产生类型转换。 浮点数和真值判断的例子,直观上看是产生了类型转换,但它们其实是 Python 的个性,是可控的、合乎预期的、并没有对原有类型造成毁坏。
退一步讲,若放宽“隐式类型转换”的含意,认为后两个例子产生了隐式类型转换,然而,它们是通过谨严的函数调用过程实现的,也不会呈现 forbidden errors,所以还是属于强查看类型。
5、其它相干的问题
前文对概念的含意以及 Python 中的体现,作了粗疏的剖析。接下来,为了逻辑与话题的完整性,咱们还须要答复几个小问题:
(1)是否以“隐式类型转换”作为强弱类型的分类根据?
明确的分类定义应该以《Type Systems》为准,它有一套针对不同 error 的分类,强弱类型其实是对于 forbidden errors 的解决分类。隐式类型转换是其显著的特色,但并不是全副,也不是惟一的判断根据。
本文为了不便了解,应用这个次要特色来划分强弱类型,然而要强调,强类型不是没有隐式类型转换,而是可能有很少且正当的隐式类型转换。
(2)如果有其它解释器令 Python 反对宽泛的隐式类型转换,那 Python 还是强类型语言么?
语言的标准规范就像是法律,而解释器是执法者。如果有谬误的执法解释,那法律还是那个法律,应该改掉谬误的执法行为;如果是法律自身有问题(造成了解释歧义和矛盾,或者该废除),那就应该批改法律,保障它的确定性(要么是强类型,要么是弱类型)。
(3)为什么说 Javascript 是弱类型?
因为它的隐式类型转换十分多、非常复杂、十分过分!比方,Javascript 中123 + null
后果为 123,123 + {}
后果为字符串“123[object Object]”。
另外,它的双等号“==”除了有根本的比拟操作,还可能产生多重的隐式类型转换,例如true==['2']
判断出的后果为 false,而true==['1']
的后果是 true,还有[]==![]
和[undefined]==false
的后果都为 true……
(4)C++ 是不是弱类型语言?
前文提到《晦涩的 Python》中将 C++ 归为强类型,但实际上它应该被归为弱类型。C++ 的类型转换是个非常复杂的话题,@樱雨楼 小姐姐曾写过一个系列文章做了零碎阐述,文章地址:如何攻克 C++ 中简单的类型转换?、详解 C++ 的隐式类型转换与函数重载!、谁说 C++ 的强制类型转换很难懂?
6、小结
强弱类型概念在网上有比拟多的争议,不仅在 Python 是如此,在 C/C++ 之类的语言更甚。
其实在学术上,这个概念早已有明确的定义,而且事实上也被很多人所接收。
那些拥护的声音大多是因为概念混用,因为他们疏忽了另一种对语言进行分类的维度;同时,还有一部分值得注意的起因,即不能认为强类型等于“齐全无隐式类型转换”或“只有没有 xxx 隐式类型转换”。
本文介绍了社区中对 Python 的支流分类,同时对几类疑似隐式类型转换的用法进行了剖析,论证出它是一种强类型语言。
文章体现了作者一贯的刨根问底精力,这是“Python 为什么”系列文章的格调,如果你喜爱本文,欢送订阅关注!
相干链接
[1] 谁通知的你们 Python 是强类型语言!站进去,保障不打你!: https://blog.csdn.net/nokiagu…
[2] Strong versus Weak Typing: https://www.artima.com/intv/s…
[3] https://en.wikipedia.org/wiki…
[4] https://en.wikipedia.org/wiki…
[5] Type Systems: http://lucacardelli.name/Pape…
[6] C 程序员十诫: http://doc.cat-v.org/henry_sp…
[7] Why is Python a dynamic language and also a strongly typed language: https://wiki.python.org/moin/…
[8] PEP-285: https://github.com/chinesehua…
[9] Type Systems: http://lucacardelli.name/Pape…
[10] Python 为什么: https://github.com/chinesehua…