当前位置: 首页 > 后端技术 > Python

python是强类型语言还是弱类型语言?

时间:2023-03-25 21:32:50 Python

作者:猫下豌豆花来源:Python猫上一篇文章分析了python为什么没有void类型的话题。文章发表后,有读者和我讨论了另一个关于类型的问题,但我们很高兴很快就出现了重大分歧。我们的主要分歧是:Python是强类型语言吗?我认为是,他认为不是。他写了一篇长文《谁告诉的你们Python是强类型语言!站出来,保证不打你!》专门重申他的观点,但错漏百出。曾经有过写python类型的想法,现在借此机会系统的梳理一下。(PS:写到快一半的时候,正好在微信读者群里讨论了“强弱类型”这个话题!在和大家讨论的过程中,验证了我的一些想法,也学到了很多东西。知识,所以本文部分内容由于群友投稿,特此感谢!)1.动态类型和静态类型以及强弱类型。很多读者应该对动态类型和静态类型比较熟悉,但是很多人会将它们与强类型和弱类型混淆。所以我们需要先做一个概念上的澄清。两组类型都特定于编程语言,但它们侧重于不同的核心问题。对于“动态类型和静态类型”的概念,其核心问题是“什么时候知道一个变量是哪种类型”?一般来说,静态类型语言在编译时确定变量类型,而动态类型语言在运行时确定变量类型。比如在某些语言中定义了函数“intfunc(inta){...}”,在编译时就可以确定它的参数和返回值都是int类型的,所以它是一个static类型;而典型的函数如Python,当定义一个函数时写“deffunc(a):...”,你并不知道参数和返回值的类型。只有在运行时调用函数,才能最终确定参数和返回值的类型,所以对于“强弱类型”的概念来说,它是动态类型。它的核心问题是“不同类型的变量是否允许隐式转换”?一般来说,强类型语言编译器进行的(合理的)隐式类型转换很少,而弱类型语言则有很多(过多的)隐式类型转换。比如Javascript中的“1000”+1会得到字符串“10001”,而“1000”-1会得到数字999,也就是说编译器会根据使用场合做两种不同类型的对象。隐式类型转换,但是类似的写法在强类型语言中会报类型错误。(数字和字符串的转换属于过度转换,下面会讲到一些合理的转换。)根据上面的定义,有人画了一张常见编程语言的分类图:按照强类型和弱类型的维度,可以归纳如下:强类型:Java、C#、Python、Ruby、Erlang(加上GO、Rust)......弱类型:C,C++,Javascript,Perl,PHP,VB...2.过去强弱类型的概念。动态类型和静态类型的概念大家基本都认可。学术讨论中存在很多争议。这里就不一一列举了。为什么会有这么多争议?主要原因之一是有人将它与动态和静态类型混合在一起。最明显的例子是GuidovanRossum在2003年参加的一次采访,主题恰好是关于Strong与WeakTyping的:然而,他们谈论的显然只是静态类型和动态类型之间的区别。采访中还引用了Java之父JamesGosling的话。从他的表述中也可以看出,他所说的“强弱类型”其实就是动态类型和静态类型的区分。还有一个经典的例子。C语言之父DennisRitchie曾说过C语言是一种“强类型但弱检查”的语言。如果和前面的定义相比,他其实指的是“statictypeweaktype”。为什么这些大佬都糊涂了?其实原因也很简单,就是当时没有明确的动态类型和静态类型,强弱类型的概念!也就是说,当时的强弱类型指的是静态类型和动态类型。维基百科在1970年代给出了强类型的定义,基本上可以简化为上面提到的静态类型:1974年,Liskov和Zilles将强类型语言定义为“每当一个对象从调用函数传递给一个被调用的函数,它的类型必须与被调用函数中声明的类型兼容。”[3]1977年,Jackson写道,“在强类型语言中,每个数据区域都有一个不同的类型,每个进程都会说明它的通信要求就这些类型而言。”[4]之前的编程语言之父们应该持有类似的概念,但是大佬们也意识到当时“强类型和弱类型”的概念不够准确,所以DennisRitchie说“强类型但是weakchecks”,而Guido在采访中也强调,Python不应该被称为弱类型,而应该说是运行时类型(runtimetyping)。但在那些早期,强类型和弱类型基本是同义词有动态类型和静态类型,这种思想至今仍然影响着很多人。3.当前强弱类型的概念。早期的编程语言分类实际上混合了动态和静态以及强弱两个维度。但是,它们不是一一对应的重叠关系,不足以表达编程语言之间的差异。因此,需要一个更清晰/更丰富的定义。有人提出了区分“类型安全”、“内存安全”等ng维度,也出现了静态检查类型和动态检查类型,与强类型和弱类型有一定的交集。直到2004年一篇综合学术论文的出现《Type Systems》(来自微软研究院,作者LucaCardelli),专门研究编程语言的不同类型系统:有一篇简短的总结如下:Stronglycheckedlanguage:Alanguagewhere运行时不会出现禁止错误(取决于禁止错误的定义)。弱检查语言:经过静态检查但不提供不存在执行错误的明确保证的语言。关键是程序对_untrappederrors_的检查强度。在一些实际已经出错的地方,弱类型的程序并没有进行捕获处理,比如C语言中的一些指针计算和转换,《C 程序员十诫》的前几个就是弱类型的问题。论文中对这些概念的定义还比较抽象。由于大部分未捕获错误都是由隐式类型转换引起的,所以第一节的定义有所演变,以隐式类型转换为判断标准。如今,“对隐式类型转换的容忍度”作为强弱类型的分类标准,是很多人的共识(虽然不够全面,也有一些不同的声音)。例如,维基百科将隐式类型转换视为弱类型的主要特征之一:弱类型语言具有更宽松的类型规则,可能会产生不可预测的结果或可能在运行时执行隐式类型转换。比如以Python为例,社区主流观点认为它是强类型语言,判断标准也是基于隐式类型转换。例子很多,比如Python的官方wiki,专门回答了WhyisPythonadynamiclanguageandalsoastronglytypedlanguage,并给出了4个回答来刻画Python的“动态强类型”:又如,在《流畅的Python》杂项中talk在第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本身的地址,而是x指向的对象的地址实际对象。比如1+True这样的数字加到Boolean类型上,是没有隐式类型转换的。因为Python中的布尔类型其实是整数类型的子类,是同一个类型!(如果有疑问,请查看PEP-285。)例如,将整数/布尔值添加到浮点数不需要在Python中进行显式类型转换。但是,它的实现过程实际上使用了数字的__add__()方法。Python中的一切都是对象,数字对象也有自己的方法。(其他语言不一定)也就是说,数字之间的算术运算实际上是一个函数调用的过程,这与其他语言中的算术运算有着本质的区别。另外,虽然不同的数字类型在计算机存储层面上差别很大,但在人眼中,它们是同一种类型(大体上划分),所以即使发生隐式类型转换,逻辑上也是可以接受的。最后再举一个例子,就是Python在if/while之后的真值判断。之前分析过它的实现原理,它会将其他类型的对象转换成布尔值。但实际上只是函数调用的结果(__bool__()和__len__()),是通过计算得到的合理结果,不属于隐式强制转换,不属于未捕获错误的范畴。所以,严格来说,前面5个例子都没有发生类型转换。浮点数和真值判断的例子,从直观上看是发生了类型转换,但实际上是Python的特性,是可控的、意料之中的,不会对原有类型造成破坏。退一步讲,如果把“隐式类型转换”的含义放宽一点,上两个例子都认为发生了隐式类型转换,但它们是通过严格的函数调用过程实现的,不会出现禁止错误,所以他们仍然属于Stronglycheck类型。5.其他相关问题上一篇文章对概念的含义及其在Python中的表现做了详细的分析。接下来,为了逻辑和题目的完整性,我们还需要回答几个小问题:(1)“隐式类型转换”能否作为强弱类型分类的依据?明确的分类定义应该是根据《Type Systems》。它有一组针对不同错误的分类。strong和weak类型实际上是对禁止错误的处理分类。隐式类型转换是其明显的特征,但不是全部,也不是判断的唯一依据。为了便于理解,本文以这个主要特征来区分强类型和弱类型,但必须强调的是,强类型并非没有隐式类型转换,只是隐式类型转换可能很少且合理。(2)如果有其他解释器让Python能够支持大范围的隐式类型转换,Python还是强类型语言吗?语言的标准规范就像法律,解释者就是执行者。执法解释有误,法律仍然是法律,错误的执法行为应予纠正;如果法则本身有问题(造成解释上的歧义和矛盾,或者应该被丢弃),那么就应该修改法则以确保其确定性(强类型或弱类型)。(3)为什么说Javascript是弱类型的?因为它有很多隐式类型转换,很复杂,也很过分!比如Javascript中123+null的结果是123,123+{}的结果就是字符串“123[objectObject]”。另外,它的双等号“==”除了基本的比较操作外,还可能进行多次隐式类型转换。比如true==['2']判断的结果为假,而true==['1']为真,[]==![]和[undefined]==false都为真...(4)C++是弱类型语言吗?前面说《流畅的Python》把C++归类为强类型,其实应该归为弱类型。C++的类型转换是一个很复杂的话题。@红雨楼女士曾写过系列文章进行系统的探讨。文章地址为:如何攻克C++中的复杂类型转换?,详解C++的隐式类型转换和函数重载!,谁说C++的强制类型转换难懂?6.小结强弱类型的概念在网上争议很大,不仅是Python,C/C++等语言也是如此。事实上,在学术上,这个概念早已被明确界定,而且事实上已经被很多人所接受。那些反对的声音大多是概念混用,因为他们忽略了语言分类的另一个维度;同时,也有一些值得注意的原因,就是强类型不能等同于“根本没有隐式类型转换”或者“只要没有xxx隐式类型转换”。本文介绍了python在社区的主流分类,同时分析了几个疑似隐式类型转换的用法,并论证了它是一门强类型语言。