甲壳虫|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 的处理分类 。 隐式类型转换是其显著的特征 , 但并不是全部 , 也不是独一的判定依据 。


推荐阅读