Python 类型检查
编程 |

    最近微软开源了静态类型检查新工具 Pyright,基于 TypeScript 开发,速度快而且不依赖 Python 运行环境。在 GitHub 上关注度很高,获得超过 3 000 个 star。有意思的是,之前 谷歌、脸书、Dropbox 也开源了各自的类型检查工具,可见各家公司都为了 Python 类型检查操碎了心。

    我不是编程语言专家,这里只是杂谈。 ^_^

动态类型

    Python 最近如日中天,尤其是运维、web 服务器开发、数值计算以及人工智能等领域。但是随着 Python 的流行,也暴露出一些问题,比如说动态类型

    Python 是 强类型动态类型检查。关于类型系统,请参考 wiki: 类型系统

    动态类型可以说是 Python 最大的优点,同时也是最大的缺点。因为动态类型以及各种语法糖,使得开发者能够更多的关注业务逻辑。然而,这会导致代码难以维护,尤其是在缺乏文档的情况下。关于 Python 是否适合大型项目的争议一直不断,例如 知乎: 在大型项目上,Python 是个烂语言吗?

官方的解决方案

    Python 官方提出了一些草案,并在 3.5 和 3.6 上实现了一些功能。标准库中引入了 typing 类型标注支持 模块,注意,文档中明确说明当前是 暂定状态,随时会变。

  1. PEP 484: 类型提示

    文档中定义了参数和返回值的类型提示规范。注意,文档中明确说明了,不会在运行时检查类型。

    While these annotations are available at runtime through the usual __annotations__ attribute, no type checking happens at runtime.

    def greeting(name: str) -> str:
        return 'Hello ' + name
    
    greeting('Guido van Rossum')
    

    对于这里的类型 name: str 只是一个标记,并不会对传入的参数做强制检查。例如,greeting(10086) 会在运行时, 'Hello ' + name 报错,而不是参数部分。返回类型 -> str 同样不会强制检查。

    也因此,一些公司在提交代码时会强制做语义分析,以避免这种情况。

  2. PEP 526: 变量注释语法

    这里定义了变量的类型提示规范,不同于 PEP 484,这个提议已经完成(Final)。

    This PEP aims at adding syntax to Python for annotating the types of variables (including class variables and instance variables), instead of expressing them through comments。

    这个协议致力于增加了一种语法,在注释中申明 Python 变量的类型(包括类变量和实例变量),而不是在注释中解释它们。

    # 'primes' is a list of integers
    primes = []  # type: List[int]
    
    # 'captain' is a string (Note: initial value is a problem)
    captain = ...  # type: str
    
    class Starship:
        # 'stats' is a class variable
        stats = {}  # type: Dict[str, int]
    
    

     

  3. PEP 544: 结构化的子类型

    这个协议当前处于草稿状态(Draft)。

    上面两个协议都是只能对明确的定义做出注解,比如参数、返回值、变量。然而 Python 的一个核心特性是 动态类型 。这个协议的目的就是,解决 Python 动态类型特性下的,一些不明确的,会在运行过程中产生的对象该如何规范的问题。

    The problem with them is that a class has to be explicitly marked to support them, which is unpythonic and unlike what one would normally do in idiomatic dynamically typed Python code.

    这个协议,假设在 typing 模块中定义一个 Protocol ,并通过继承 Protocol 来实现对类的定义。

    # 注意:下面代码不能运行。
    from typing import Protocol, List
    
    class Template(Protocol):
        name: str        # This is a protocol member
        value: int = 0   # This one too (with default)
    
        def method(self) -> None:
            self.temp: List[int] = [] # Error in type checker
    
    class Concrete:
        def __init__(self, name: str, value: int) -> None:
            self.name = name
            self.value = value
    
    var: Template = Concrete('value', 42)  # OK
    

第三方开源的检查工具

    许多公司都有大量的 Python 代码。对于一些大公司会实现自己的代码规范,结合编辑器使用,从而提高代码质量。

    比如 Google 的 pytype、Facebook 的 Pyre、微软的 Pyright、Dropbox 的 PyAnnotate、以及 Python 社区的mypy。就像文章开头提到的,Pyright 比较热门,毕竟微软做编辑器的功力还是很深厚的,结合 vs code 写起来感觉很不错。

    大公司会有规范的开发流程,但是小公司,往往只能靠开发者自己的理解。回想起当年同事离职之后我只能重写他的项目时候的痛苦。

Python 的另一个问题:运行效率

    一般人们说的 Python 是指 CPython,CPython 的实现较为糟糕,运行慢,多线程支持不好等问题。这使得Python 运行效率一直都备受指责。需要执行效率高的地方都用 C 语言实现,Python 绑定 API。也有人尝试不同的方式来提高效率,参照Alternative Python Implementations 。大多都是草草收尾,PyPy 应该是仅存的还在坚持的项目, 不过依然没有流行起来。最近有人用 Rust 实现 Python 解释器,RustPython,有意思。

    开发者似乎逐渐选择另外一种结局途径,那就是 换一种编程语言。Google 推出的 Golang 正在 web 开发,运维等领域大展身手。

最后

    Python 还是一个不错的语言,希望 Python 社区能够改进这两个问题,毕竟其他公司(社区)很难从语言本身来解决问题。