|
|
Python中的文档测试与类型标注
|
|
|
|
|
|
|
|
|
# 所需环境
|
|
|
- Python 3.7
|
|
|
- IDE (这里使用VS Code)
|
|
|
|
|
|
|
|
|
# Python测试
|
|
|
- `unittest`模块
|
|
|
- `doctest`模块
|
|
|
|
|
|
## 比较
|
|
|
|测试方法|应用方法|功能|特点|
|
|
|
|--|--|--|--|
|
|
|
|`unittest`|`assert`|专注于测试|使用复杂+精确|
|
|
|
|`doctest`|`testmod`, `testfile`方法|文档+测试|使用简单+不精确(全部依赖于字符串,要考虑字符串转义以及`__repr__`方法)+支持`unittest`|
|
|
|
|
|
|
## `doctest`主要用途
|
|
|
- 检查docstring中的示例
|
|
|
- 回归测试
|
|
|
- 为包编写教程文档
|
|
|
|
|
|
## `doctest`模块使用
|
|
|
### `testmod()`方法
|
|
|
- 见`doctest_example.py`
|
|
|
|
|
|
### `testfile()`方法
|
|
|
- 见`doctest_example.py`
|
|
|
- 替代品 `python -m doctest -v doctest_example.txt`
|
|
|
|
|
|
## 输出内容
|
|
|
- 只将测试失败的用例和原因输出到stdout
|
|
|
- 若还要输出正确的,在CLI中添加`-v`参数
|
|
|
|
|
|
## 例外
|
|
|
- 见`doctest_exceptions_example.py`
|
|
|
- 常用
|
|
|
- `# doctest: +ELLIPSIS`
|
|
|
- `# doctest: +SKIP`
|
|
|
- [doctest Directives](https://docs.python.org/zh-cn/3.7/library/doctest.html#directives)
|
|
|
|
|
|
## doctest原理
|
|
|
- 见`how_doctest_works.py`
|
|
|
- 对象(`module`, `class`, `function`)的`__doc__`属性
|
|
|
- `globs`参数 注入测试执行上下文
|
|
|
- 可以是自己构建的`dict`可以是`locals()`, `globals()`
|
|
|
|
|
|
## doctest的好处
|
|
|
- 测试
|
|
|
- 自动生成文档
|
|
|
- `pydoc`模块读取`__doc__`
|
|
|
- 可以通过IDE直接看一个对象
|
|
|
- TDD闭环
|
|
|
- 若需要新特性,则添加一个新的测试用例
|
|
|
- 运行所有测试用例,新的用例会失败
|
|
|
- 编写通过新测试用例的最简单的代码,并且使得所有测试用例通过
|
|
|
- 根据需要进行重构
|
|
|
|
|
|
# Python类型标注
|
|
|
## `typing`模块
|
|
|
- 最近更新很快,3.7
|
|
|
- [PEP 484 输入提示](https://www.python.org/dev/peps/pep-0484/)
|
|
|
- [PEP 526 变量注释](https://www.python.org/dev/peps/pep-0526/)
|
|
|
- [PEP 483 输入提示理论](https://www.python.org/dev/peps/pep-0483/)
|
|
|
|
|
|
## 重要
|
|
|
- 类型标注只用于静态检查,运行时不会强制检查函数标注和变量注释
|
|
|
|
|
|
## `typing`模块使用
|
|
|
- [类型别名](https://docs.python.org/zh-cn/3.7/library/typing.html#type-aliases)
|
|
|
- 简化复杂类型的签名
|
|
|
- [NewType](https://docs.python.org/zh-cn/3.7/library/typing.html#newtype)
|
|
|
|
|
|
### 常用类型
|
|
|
- `None`
|
|
|
- `Tuple[int, ...]`
|
|
|
- `List[int]`, `Sequence[int]`
|
|
|
- `Dict[str, str]`, `Mapping[str, str]`
|
|
|
- `Union[int, str]`
|
|
|
- `Optional[int]`等价于`Union[int, None]`
|
|
|
- `Callable[[Arg1Type, Arg2Type], ReturnType]`
|
|
|
- `Iterable[int]`, `Iterator[int]`
|
|
|
- `Generator[YieldType, SendType, ReturnType]`
|
|
|
|
|
|
### 泛型与用户自定义泛型
|
|
|
- 方括号
|
|
|
- `T = TypeVar('T')`
|
|
|
- `type(..., (Generic[T], ...), dict=...)`
|
|
|
|
|
|
## `typing`原理
|
|
|
- 对象的`__annotations__`属性
|
|
|
|
|
|
## 好处
|
|
|
- 继承静态检查的部分好处
|
|
|
- 在IDE中发现错误
|
|
|
- 有类型签名,开发体验好
|
|
|
- 复杂/不重要的变量类型标注可以写`Any`或者不写,保持Python的灵活
|
|
|
|
|
|
## 坏处
|
|
|
- 继承静态检查的坏处
|
|
|
- 死板,`pylance`几乎一直会犯错
|
|
|
- 复杂
|
|
|
- 某个变量的类型标注是固定的,不能改变
|
|
|
- 静态检查依赖于你自己写的类型标注,首先保证自己写对
|
|
|
- Python类型标注在3.5后才有,并且更新很快,许多内置包的类型标注不对
|
|
|
|
|
|
# 参考
|
|
|
- [doctest --- 测试交互性的Python示例](https://docs.python.org/zh-cn/3.7/library/doctest.html)
|
|
|
- [typing --- 类型标注支持](https://docs.python.org/zh-cn/3.7/library/typing.html)
|
|
|
|