0、前言
我在上一篇文案中分析了 为何 Python 无 void 类型 的专题,在文案发布后,有读者跟我讨论起了另一个关于类型的问题,然则,咱们火速就显现了重大歧义。
咱们重点的歧义就在于:Python 到底是不是强类型语言? 我认为是,而他认为不是。
他写了一篇很长的文案《谁告诉的你们Python是强类型语言!站出来,保准不打你!》,专门重申了他的观点,但可惜错漏百出。
我曾有想法要写写关于 Python 类型的问题,此刻借着这个机会,就来系统地梳理一下吧。
(PS:在我写作进行到差不多一半的时候,微X读者群里恰好亦讨论到“强弱类型”的专题!在与大众讨论时,我的有些想法得到了验证,同期我亦学到了很多新知识,因此本文的部分内容有群友们的功劳,特此鸣谢!)
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)大大都是因为隐式类型转换导致,因此又演化出了第1节中的定义,以隐式类型转换做为判断标准。
如今将“对隐式类型转换的容忍度”做为强弱类型的归类标准,已然是非常多人的共识(虽然不足全面,况且有有些区别的声音)。
例如,维基百科就把隐式类型转换做为弱类型的重点特点之一:
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/nokiaguy/article/details/108218260
[2] Strong versus Weak Typing: https://www.artima.com/intv/strongweak.html
[3] https://en.wikipedia.org/wiki/Strong_and_weak_typing#cite_note-2
[4] https://en.wikipedia.org/wiki/Strong_and_weak_typing#cite_note-3
[5] Type Systems: http://lucacardelli.name/Papers/TypeSystems.pdf
[6] C 程序员十诫: http://doc.cat-v.org/henry_spencer/ten-commandments
[7] Why is Python a dynamic language and also a strongly typed language: https://wiki.python.org/moin/Why%20is%20Python%20a%20dynamic%20language%20and%20also%20a%20strongly%20typed%20language
[8] PEP-285: https://github.com/chinesehuazhou/peps-cn/blob/master/StandardsTrack/285--%E6%B7%BB%E5%8A%A0%E5%B8%83%E5%B0%94%E7%B1%BB%E5%9E%8B.md
[9] Type Systems: http://lucacardelli.name/Papers/TypeSystems.pdf
[10] Python为何: https://github.com/chinesehuazhou/python-whydo
|