RPython中的静态类型是什么?
人们常常说 RPython(Python的一种子集)是静态类型的。(比如在维基百科上。)
一开始,我在想他们是怎么把这个特性加到Python里的,觉得可能是要求在每个函数的开头加上像 assert isinstance(arg1, ...)
这样的语句(但我真的不太相信这个)。
然后我看了一些RPython的代码,发现它看起来根本不像是静态类型的。在很多情况下,编译器可能能证明一个函数的参数只能是某几种类型,但并不是所有情况下都能做到。
比如,这就是 string.split
的RPython实现:
def split(value, by, maxsplit=-1):
bylen = len(by)
if bylen == 0:
raise ValueError("empty separator")
res = []
start = 0
while maxsplit != 0:
next = value.find(by, start)
if next < 0:
break
res.append(value[start:next])
start = next + bylen
maxsplit -= 1 # NB. if it's already < 0, it stays < 0
res.append(value[start:len(value)])
return res
在关于RPython的PyPy文档中提到:“变量应该只包含一种类型的值”。
那么,函数参数也算是变量吗?或者说RPython到底是怎么静态类型的?这是不是说错了呢?
2 个回答
是的,它是静态类型的。在你的例子中,所有的变量类型都没有改变,这符合RPython的要求。RPython并没有一个正式的定义,它的限制也在不断变化,不过文档还是一个很好的入门资料。在稍微了解一下之后,最好的办法就是尝试翻译一些代码,你会很快明白哪些可以做,哪些不可以做!
那么,函数的参数也算变量吗?
当然算。在几乎所有编程语言中,都是这样。
那RPython到底是以什么方式静态类型的?或者说这其实是说错了?
这个说法是对的。RPython不是Python。 它是Python的一个子集,可以作为Python代码运行。但是,当你真正编译RPython代码时,很多动态特性就被去掉了(不过这只是在导入之后,所以你仍然可以使用类元、从字符串生成代码等,这在某些模块中效果很好),这样编译器(这不是 Python编译器,而是与传统编译器有很大不同的编译器;具体可以查看相关文档)就能确定类型是静态的。更准确地说,使用动态特性的代码可以通过解析器,但最终会在某个时刻导致类型错误。
在很多情况下,编译器可能能证明一个函数参数只能是某些特定类型,但并不是所有情况下都是这样。
当然不是。很多代码并不是静态类型的,还有一些静态类型的代码当前的注释工具无法证明是静态类型的。但是当遇到这样的代码时,就会出现编译错误,没得说。
有几个重要的点需要了解:
类型是推断出来的,而不是明确写出来的(大部分情况下是这样;我相信有一些函数需要断言来帮助注释工具)。静态类型并不意味着类型必须写出来(那叫显式类型),而是每个表达式(包括变量)都有一个单一的类型,这个类型是不会改变的。
所有的分析都是在整个程序的基础上进行的!你不能为一个函数
def add(a, b): return a + b
推断出一个(非泛型)类型(因为参数可能是整数、浮点数、字符串、列表等等),但是如果这个函数用整数参数调用(比如整数字面量或之前被推断为包含整数的变量),那么就可以确定a
和b
(以及由于+
的类型,add
的结果)也是整数。PyPy库中的所有代码都不是RPython。例如,有一些代码生成器(比如在
rlib.parsing
中)是在编译时运行并生成RPython代码,但它们本身并不是RPython(顺便提一下,通常会有一个"NOT_RPYTHON"
的文档字符串)。此外,标准库的大部分是用完整的Python编写的(大多直接取自CPython)。
关于整个翻译和类型系统是如何工作的,有很多非常有趣的资料。例如,RPython工具链描述了翻译过程,包括类型推断,而RPython类型系统则描述了使用的类型系统。