@Lenciel

Python in 2020 (4) - 类型检查

编程语音的类型系统其实是非常重要的。

但作为一种弱类型语言,Python 的类型检查是非常困难的。但自从 3.5 版本加入了 type annotation,很多工具变得强大了起来。

目前,mypy 是这个领域最值得关注的。几个大厂也各自有自己的实现[^1],有些 IDE 比如 PyCharm 还自带一个类型检查工具,但这里主要说一下 mypy 的使用。

目录

静态类型检查

安装 mypy:

$ poetry add --dev mypy

在 Nox 里面添加一个 session:

@nox.session(python=["3.8", "3.7"])
def mypy(session):
    args = session.posargs or locations
    install_with_constraints(session, "mypy")
    session.run("mypy", *args)

然后就可以运行了:

$ nox -rs mypy

为了控制可以编辑 ‌mypy.ini 配置文件,比如:

# mypy.ini
[mypy]

[mypy-nox.*,pytest]
ignore_missing_imports = True

类型声明和检查

如果类型检查只是装个工具扫描现有的类型意义就不大了。它更大的作用是支持自定义类型并检查。

假设我们有下面这样的函数,返回的是一个页面的 json 格式对象:

def get_page(): ...

我们想要给它声明一个类型,但它不是 Python 自带的 str/list/dict 等类型可以覆盖的,所以我们可以声明为一个使用 dataclasses 定义的 Page:

from dataclasses import dataclass


@dataclass
class Page:
    title: str
    extract: str

def get_page() -> Page: ...

那么,在测试的时候如何进行 Page 的构造呢?可以使用 marshallowdessert 。前者是一个数据序列化/反序列化的工具,后者则是以 marshallow 打底使用 dataclass 的 annotation 生成序列化 schema 的工具。

安装:

$ poetry add desert marshmallow

添加依赖:

# mypy.ini
[mypy-desert,marshmallow,nox.*,pytest]
ignore_missing_imports = True

这样我们就可以代码里面使用 dessert :

from dataclasses import dataclass

import click
import desert
import marshmallow
import requests


API_URL: str = "https://test.com/api/rest_v1/page/random/summary"


@dataclass
class Page:
    title: str
    extract: str


schema = desert.schema(Page, meta={"unknown": marshmallow.EXCLUDE})


def get_page() -> Page:
    url = API_URL.format()

    try:
        with requests.get(url) as response:
            response.raise_for_status()
            data = response.json()
            return schema.load(data)
    except (requests.RequestException, marshmallow.ValidationError) as error:
        message = str(error)
        raise click.ClickException(message)

这样在测试中也就可以直接进行类型检查了:

# tests/test_get_page.py
def test_get_page(mock_requests_get):
    page = test.get_page()
    assert isinstance(page, test.Page)

欢迎留言