- 人工智能
- 计算机视觉
- 深度学习
【免费下载链接】mmcv
OpenMMLab Computer Vision Foundation
本文是 OpenMMLab 计算机视觉基础库 MMCV 的代码风格官方指南(对应仓库文档 docs/zh_cn/community/code_style.md)的深度展开版。它面向两类读者:一是准备向 MMCV 提交 Pull Request 的贡献者,需要让代码通过 pre-commit、CI 与 reviewer 的审查;二是希望在项目内建立统一代码规范的团队。读完本文,你将掌握 MMCV 所遵循的 PEP 8 与 Google 风格取舍、命名与 docstring 的具体写法、类型注解与 mypy 的使用方法,以及 pre-commit 工具链的完整配置,从而写出与仓库风格一致、可维护、易审查的 Python 代码。
一、代码规范的两个标准与 MMCV 的取舍
1.1 PEP 8 —— Python 官方代码规范
PEP 8 是 Python 官方的代码风格指南,是 OpenMMLab 算法库首选的代码规范(见 docs/zh_cn/community/contributing.md 中"代码风格"一节)。它主要覆盖以下几方面内容:
- 代码布局:规定 Python 中空行、断行以及导入相关的风格。一个常见疑问是:当代码较长无法在一行内写完时,何处可以断行?PEP 8 给出了明确的换行规则。
- 表达式:规定表达式中空格的使用方式。
- 尾随逗号:当列表较长、无法一行写下而写成逐行列表时,推荐在末项之后也加上逗号,便于后续追加选项和版本控制对比。
尾随逗号的正反例:
# Correct: FILES = ['setup.cfg', 'tox.ini'] # Correct: FILES = [ 'setup.cfg', 'tox.ini', ] # Wrong: FILES = ['setup.cfg', 'tox.ini',] # Wrong: FILES = [ 'setup.cfg', 'tox.ini' ]- 命名、注释、类型注解:这些内容的详细规范是本文后续章节的主体。
1.2 项目内一致性优先于 PEP 8
"A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important."
PEP 8 的规范并不是绝对的,项目内的一致性要优先于 PEP 8 的规范。OpenMMLab 各个项目都在setup.cfg中设定了代码规范设置,贡献者应遵照这些设置。例如 PEP 8 中有如下例子:
# Correct: hypot2 = x*x + y*y # Wrong: hypot2 = x * x + y * y这一规范本意是指示运算优先级,但 OpenMMLab 的设置中通常没有启用 yapf 的ARITHMETIC_PRECEDENCE_INDICATION选项,因而格式规范工具不会按照该推荐样式强制格式化,一切以项目实际配置为准。打开仓库根目录的 setup.cfg 可以看到:
[yapf] based_on_style = pep8 blank_line_before_nested_class_or_def = true split_before_expression_after_opening_paren = true [isort] line_length = 79 multi_line_output = 0 extra_standard_library = pkg_resources,setuptools,logging,os,warnings,abc known_first_party = mmcv known_third_party = addict,cv2,matplotlib,numpy,onnx,packaging,pytest,pytorch_sphinx_theme,scipy,sphinx,torch,torchvision,yaml,yapf no_lines_before = STDLIB,LOCALFOLDER default_section = THIRDPARTY这些配置说明:yapf 基于 pep8 风格,isort 的每行长度限制为 79 字符,mmcv被标记为 first party,而 torch、numpy 等被标记为 third party,导入顺序会自动按标准库 → 第三方 → 本地模块排列。
1.3 Google 开源项目风格指南
Google 开源项目风格指南(Google Python Style Guide)比 PEP 8 提供了更详尽的指导,包含语言规范和风格规范两个部分:
- 语言规范:对 Python 中许多语言特性(异常、Lambda 表达式、列表推导式、metaclass 等)进行优缺点分析并给出使用意见。
- 风格规范:大部分约定建立在 PEP 8 基础上,同时有更细的约定,如函数长度、TODO 注释、文件与 socket 对象的访问等。
MMCV 推荐将 Google 指南作为开发参考,但不必严格遵照,原因有二:
- 该指南存在 Python 2 兼容需求,例如要求所有无基类的类显式继承
object,而在仅使用 Python 3 的环境中这一要求不必要,按本项目惯例即可; - OpenMMLab 项目作为框架级开源软件,不必对高级技巧过于避讳,尤其是 MMCV;但使用这些技巧前应认真考虑是否真的有必要,并寻求其他开发者的广泛评估。
需要特别留意的一处差异是包的导入:Google 指南要求导入本地包时使用路径全称,且每个模块单独成行。这在 MMCV 中通常不必要,也不符合项目开发惯例,本项目约定如下:
# Correct from mmcv.cnn.bricks import (Conv2d, build_norm_layer, DropPath, MaxPool2d, Linear) from ..utils import ext_loader # Wrong from mmcv.cnn.bricks import Conv2d, build_norm_layer, DropPath, MaxPool2d, \ Linear # 使用括号进行连接,而不是反斜杠 from ...utils import is_str # 最多向上回溯一层,过多的回溯容易导致结构混乱要点:跨行导入用括号连接而非反斜杠;相对导入最多向上回溯一层,避免结构混乱。
1.4 OpenMMLab 的落地:pre-commit 自动格式化
OpenMMLab 项目使用 pre-commit 工具自动格式化代码,具体安装与使用见 docs/zh_cn/community/contributing.md 的"配置 pre-commit"与"代码风格"章节。核心命令如下(须在 MMCV 目录下执行):
pip install -U pre-commit pre-commit install pre-commit run --all-files中国用户若因网络问题安装失败,可使用国内镜像配置:
pre-commit install -c .pre-commit-config-zh-cn.yaml pre-commit run --all-files -c .pre-commit-config-zh-cn.yaml仓库根目录的 .pre-commit-config.yaml 是本项目实际的钩子配置,包含以下工具链:
| 钩子 | 作用 |
|---|---|
| flake8 | Python 官方推荐的代码规范检查工具,是多个检查工具的封装 |
| isort | 自动调整模块导入顺序 |
| yapf | Google 发布的代码格式化工具 |
| trailing-whitespace / mixed-line-ending / end-of-file-fixer | 去除行尾空白、统一行尾符(LF)、修复文件末尾换行 |
| check-yaml / check-merge-conflict | 校验 YAML 语法、检查未解决的合并冲突 |
| requirements-txt-fixer | 调整requirements.txt中包的顺序 |
| double-quote-string-fixer / fix-encoding-pragma / pyupgrade | 统一引号、移除编码声明、升级为 Python 3 语法 |
| codespell | 检查单词拼写 |
| mdformat | 检查并格式化 Markdown 文件(配合 mdformat-openmmlab) |
| docformatter | 格式化 docstring(--in-place --wrap-descriptions 79) |
| check-copyright | 检查文件版权头(针对 mmcv、tests,排除 mmcv/ops) |
| mypy | 静态类型检查(排除 tests 与 docs 目录) |
其中 C++ 和 CUDA 代码遵循 Google C++ Style Guide(clang-format 钩子在配置中以注释形式预留)。如果提交的代码不符合代码风格规范,pre-commit 会发出警告并自动修复部分错误;若想临时绕开检查,可在git commit时加上--no-verify(仅用于临时提交,最终推送的代码必须通过 pre-commit 检查)。
二、命名规范
2.1 命名规范的重要性
优秀的命名是良好代码可读性的基础。基础命名规范对各类变量做约束,使读者能根据名字判断它是一个类、局部变量还是全局变量;而优秀的命名则要求作者对变量功能有清晰认识与良好表达能力,让读者仅凭名称即可理解其含义。
2.2 基础命名规范
| 类型 | 公有 | 私有 |
|---|---|---|
| 模块 | lower_with_under | _lower_with_under |
| 包 | lower_with_under | |
| 类 | CapWords | _CapWords |
| 异常 | CapWordsError | |
| 函数(方法) | lower_with_under | _lower_with_under |
| 函数 / 方法参数 | lower_with_under | |
| 全局 / 类内常量 | CAPS_WITH_UNDER | _CAPS_WITH_UNDER |
| 全局 / 类内变量 | lower_with_under | _lower_with_under |
| 变量 | lower_with_under | _lower_with_under |
| 局部变量 | lower_with_under |
注意:
- 尽量避免变量名与保留字冲突;若不可避免,可使用一个后置下划线,如
class_; - 尽量不要使用过于简单的命名,约定俗成的循环变量
i、文件变量f、错误变量e除外; - 不会被用到的变量可命名为
_,逻辑检查器会将其忽略。
2.3 命名技巧
良好的变量命名需保证三点:含义准确、没有歧义;长短适中;前后统一。
# Wrong class Masks(metaclass=ABCMeta): # 命名无法表现基类;Instance or Semantic? pass # Correct class BaseInstanceMasks(metaclass=ABCMeta): pass # Wrong,不同地方含义相同的变量尽量用统一的命名 def __init__(self, inplanes, planes): pass def __init__(self, in_channels, out_channels): pass常见的函数命名方法:
- 动宾命名法:
crop_img,init_weights - 动宾倒置命名法:
imread,bbox_flip
注意函数命名与参数的顺序,保证主语在前、符合语言习惯:
check_keys_exist(key, container)check_keys_contain(container, key)
还要避免非常规或未统一约定的缩写,如nb→num_blocks,in_nc→in_channels。
三、docstring 规范
3.1 为什么要写 docstring
docstring 是对一个类、一个函数功能与 API 接口的详细描述,有两个功能:
- 帮助其他开发者了解代码功能,方便 debug 和复用代码;
- 在 Readthedocs 文档中自动生成相关 API reference 文档,帮助不了解源代码的社区用户使用功能。
3.2 如何写 docstring
与普通注释不同,规范的 docstring 有严格的格式要求,以便 Python 解释器与 sphinx 进行文档解析(docstring 的基本约定见 PEP 257)。MMCV 参考格式为 Google 风格,同时结合了 OpenMMLab 自身惯例。以下按模块、类、方法三种场景给出标准格式。
1. 模块文档
代码风格规范推荐为每个模块(Python 文件)编写 docstring,但目前 OpenMMLab 项目大部分没有此类 docstring,因此不做硬性要求。参考格式:
"""A one line summary of the module or program, terminated by a period. Leave one blank line. The rest of this docstring should contain an overall description of the module or program. Optionally, it may also contain a brief description of exported classes and functions and/or usage examples. Typical usage example: foo = ClassFoo() bar = foo.FunctionBar() """2. 类文档
类文档是贡献者最常编写的类型。按照 OpenMMLab 惯例,这里使用了与 Google 风格不同的写法:不使用 Attributes 描述类属性,而是使用 Args 描述__init__函数的参数。Args 中遵照parameter (type): Description.格式描述每个参数的类型与功能;多种类型可用(float or str)写法;可以为 None 的参数写为(int, optional)。
class BaseRunner(metaclass=ABCMeta): """The base class of Runner, a training helper for PyTorch. All subclasses should implement the following APIs: - ``run()`` - ``train()`` - ``val()`` - ``save_checkpoint()`` Args: model (:obj:`torch.nn.Module`): The model to be run. batch_processor (callable, optional): A callable method that process a data batch. The interface of this method should be ``batch_processor(model, data, train_mode) -> dict``. Defaults to None. optimizer (dict or :obj:`torch.optim.Optimizer`, optional): It can be either an optimizer (in most cases) or a dict of optimizers (in models that requires more than one optimizer, e.g., GAN). Defaults to None. work_dir (str, optional): The working directory to save checkpoints and logs. Defaults to None. logger (:obj:`logging.Logger`): Logger used during training. Defaults to None. (The default value is just for backward compatibility) meta (dict, optional): A dict records some import information such as environment info and seed, which will be logged in logger hook. Defaults to None. max_epochs (int, optional): Total training epochs. Defaults to None. max_iters (int, optional): Total training iterations. Defaults to None. """ def __init__(self, model, batch_processor=None, optimizer=None, work_dir=None, logger=None, meta=None, max_iters=None, max_epochs=None): ...补充要求:
- 在算法实现的主体类中,建议加入原论文链接;如果参考了其他开源代码实现,应加入
modified from;直接复制了其他代码库实现,则应加入copied from,并注意源码的 License; - 如有必要,可通过
.. math::加入数学公式。
# 参考实现 # This func is modified from `detectron2 # <...>`_. # 复制代码 # This code was copied from the `ubelt # library<...>`_. # 引用论文 & 添加公式 class LabelSmoothLoss(nn.Module): r"""Initializer for the label smoothed cross entropy loss. Refers to `Rethinking the Inception Architecture for Computer Vision <...>`_. This decreases gap between output scores and encourages generalization. Labels provided to forward can be one-hot like vectors (NxC) or class indices (Nx1). And this accepts linear combination of one-hot like labels from mixup or cutmix except multi-label task. Args: label_smooth_val (float): The degree of label smoothing. num_classes (int, optional): Number of classes. Defaults to None. mode (str): Refers to notes, Options are "original", "classy_vision", "multi_label". Defaults to "classy_vision". reduction (str): The method used to reduce the loss. Options are "none", "mean" and "sum". Defaults to 'mean'. loss_weight (float): Weight of the loss. Defaults to 1.0. Note: if the ``mode`` is "original", this will use the same label smooth method as the original paper as: .. math:: (1-\epsilon)\delta_{k, y} + \frac{\epsilon}{K} where :math:`\epsilon` is the ``label_smooth_val``, :math:`K` is the ``num_classes`` and :math:`\delta_{k,y}` is Dirac delta, which equals 1 for k=y and 0 otherwise. if the ``mode`` is "classy_vision", this will use the same label smooth method as the `facebookresearch/ClassyVision <...>`_ repo as: .. math:: \frac{\delta_{k, y} + \epsilon/K}{1+\epsilon} if the ``mode`` is "multi_label", this will accept labels from multi-label task and smoothing them as: .. math:: (1-2\epsilon)\delta_{k, y} + \epsilon """注意 reStructuredText 中三种引号功能不同:here(双反引号)表示一段代码;here(单反引号)表示斜体;"here"(双引号)无特殊含义,一般表示字符串。其中单反引号的用法与 Markdown 不同,需要多加留意。另外还有:obj:type`` 这种更规范的表示类的写法,鉴于长度不做特别要求,一般仅用于表示非常用类型。
3. 方法(函数)文档
函数文档与类文档结构基本一致,但需要加入返回值文档。对于较复杂的函数和类,可使用Examples字段加入示例;如果需要给参数加入较长备注,可加入Note字段。示例最好能直接在 Python 交互式环境中运行并给出对应结果;多个示例可用注释分隔说明。
def import_modules_from_strings(imports, allow_failed_imports=False): """Import modules from the given list of strings. Args: imports (list | str | None): The given module names to be imported. allow_failed_imports (bool): If True, the failed imports will return None. Otherwise, an ImportError is raise. Defaults to False. Returns: List[module] | module | None: The imported modules. All these three lines in docstring will be compiled into the same line in readthedocs. Examples: >>> osp, sys = import_modules_from_strings( ... ['os.path', 'sys']) >>> import os.path as osp_ >>> import sys as sys_ >>> assert osp == osp_ >>> assert sys == sys_ """ ...如果函数接口在某个版本发生了变化,需要在 docstring 中加入说明,必要时添加Note或Warning:
class CheckpointHook(Hook): """Save checkpoints periodically. Args: out_dir (str, optional): The root directory to save checkpoints. If not specified, ``runner.work_dir`` will be used by default. If specified, the ``out_dir`` will be the concatenation of ``out_dir`` and the last level directory of ``runner.work_dir``. Defaults to None. `Changed in version 1.3.15.` file_client_args (dict, optional): Arguments to instantiate a FileClient. See :class:`mmcv.fileio.FileClient` for details. Defaults to None. `New in version 1.3.15.` Warning: Before v1.3.15, the ``out_dir`` argument indicates the path where the checkpoint is stored. However, in v1.3.15 and later, ``out_dir`` indicates the root directory and the final path to save checkpoint is the concatenation of out_dir and the last level directory of ``runner.work_dir``. Suppose the value of ``out_dir`` is "/path/of/A" and the value of ``runner.work_dir`` is "/path/of/B", then the final path will be "/path/of/A/B". """如果参数或返回值里带有需要展开描述字段的 dict,应采用如下格式:
def func(x): r""" Args: x (None): A dict with 2 keys, ``padded_targets``, and ``targets``. - ``targets`` (list[Tensor]): A list of tensors. Each tensor has the shape of :math:`(T_i)`. Each element is the index of a character. - ``padded_targets`` (Tensor): A tensor of shape :math:`(N)`. Each item is the length of a word. Returns: dict: A dict with 2 keys, ``padded_targets``, and ``targets``. - ``targets`` (list[Tensor]): A list of tensors. Each tensor has the shape of :math:`(T_i)`. Each element is the index of a character. - ``padded_targets`` (Tensor): A tensor of shape :math:`(N)`. Each item is the length of a word. """ return x3.3 docstring 与 readthedocs 渲染
为了生成 readthedocs 文档,docstring 需要按照 reStructuredText 文档格式编写,否则会产生文档渲染错误。在提交 PR 前,最好生成并预览文档效果,方法见 docs/zh_cn/community/contributing.md 的"文档渲染"指引:
pip install -r requirements/docs.txt cd docs/zh_cn/ # or docs/en make html # check file in ./docs/zh_cn/_build/html/index.html语法规范参考 reStructuredText Primer(Sphinx 官方文档)与 Example Google Style Python Docstrings(sphinxcontrib-napoleon 文档)。OpenMMLab 使用 pytorch_sphinx_theme 与 napoleon 扩展(见 requirements/docs.txt 与 docs/zh_cn/conf.py)将上述 Google 风格的 docstring 自动渲染为 API reference 页面(如 docs/zh_cn/api/ 下各模块的 .rst 文件)。
四、注释规范
4.1 为什么要写注释
对于一个开源项目,团队合作以及社区之间的合作必不可少,因此尤其要重视合理注释。不写注释的代码,可能过几个月连作者自己也难以理解,造成额外的阅读和修改成本。
4.2 如何写注释
最需要写注释的是代码中技巧性的部分。"如果你在下次代码审查的时候必须解释一下,那么你应该现在就给它写注释。对于复杂的操作,应该在其操作开始前写上若干行注释;对于不是一目了然的代码,应在其行尾添加注释。"(Google 开源项目风格指南)
# We use a weighted dictionary search to find out where i is in # the array. We extrapolate position based on the largest num # in the array and the array size and then do binary search to # get the exact number. if i & (i-1) == 0: # True if i is 0 or a power of 2.两条硬性规则:
- 为了提高可读性,注释应至少离开代码2 个空格;
- 绝不要描述代码。"假设阅读代码的人比你更懂 Python,他只是不知道你的代码要做什么。"(Google 开源项目风格指南)
# Wrong: # Now go through the b array and make sure whenever i occurs # the next element is i+1 # Wrong: if i & (i-1) == 0: # True if i bitwise and i-1 is 0.在注释中可以使用 Markdown 语法(开发人员通常熟悉 Markdown,便于交流理解),如用单反引号表示代码和变量——注意不要与 docstring 中的 reStructuredText 语法混淆:
# `_reversed_padding_repeated_twice` is the padding to be passed to # `F.pad` if needed (e.g., for non-zero padding types that are # implemented as two ops: padding + conv). `F.pad` accepts paddings in # reverse order than the dimension. self._reversed_padding_repeated_twice = _reverse_repeat_tuple(self.padding, 2)4.3 注释示例
代码风格文档给出了两类典型注释场景:
- 复杂逻辑结构:对优先级关系进行说明。例如 registry 构建函数的选择逻辑:
# self.build_func will be set with the following priority: # 1. build_func # 2. parent.build_func # 3. build_from_cfg if build_func is None: if parent is not None: self.build_func = parent.build_func else: self.build_func = build_from_cfg else: self.build_func = build_func- bug 修复的特殊处理:附带相关 issue 链接,帮助其他人了解 bug 背景。例如 checkpoint 保存时针对 PyTorch 1.6 文件格式变更的兼容处理:
def _save_ckpt(checkpoint, file): # The 1.6 release of PyTorch switched torch.save to use a new # zipfile-based file format. It will cause RuntimeError when a # checkpoint was saved in high version (PyTorch version>=1.6.0) but # loaded in low version (PyTorch version<1.6.0). More details at # https://github.com/open-mmlab/mmpose/issues/904 if digit_version(TORCH_VERSION) >= digit_version('1.6.0'): torch.save(checkpoint, file, _use_new_zipfile_serialization=False) else: torch.save(checkpoint, file)五、类型注解
5.1 为什么要写类型注解
类型注解是对函数中变量的类型做限定或提示,为代码的安全性提供保障、增强可读性、避免出现类型相关的错误。Python 对类型没有强制限制,类型注解只起提示作用:IDE 会解析注解并在调用代码时给出类型提示;类型注解检查工具(如 mypy)则会根据注解对代码中可能出现的问题进行检查,减少 bug 的出现。
通常不需要注释模块中的所有函数,按以下原则权衡:
- 公共的 API 需要注释;
- 在代码的安全性、清晰性和灵活性之间权衡是否注释;
- 对于容易出现类型相关错误的代码进行注释;
- 难以理解的代码请进行注释;
- 若代码中的类型已经稳定,可以进行注释。对于一份成熟的代码,多数情况下即使注释了所有函数,也不会丧失太多灵活性。
5.2 函数 / 方法类型注解
通常不对self和cls注释。根据行宽有多种写法:
from typing import Optional, List, Tuple # 全部位于一行 def my_method(self, first_var: int) -> int: pass # 另起一行 def my_method( self, first_var: int, second_var: float) -> Tuple[MyLongType1, MyLongType1, MyLongType1]: pass # 单独成行(具体的应用场合与行宽有关,建议结合 yapf 自动化格式使用) def my_method( self, first_var: int, second_var: float ) -> Tuple[MyLongType1, MyLongType1, MyLongType1]: pass # 引用尚未被定义的类型 class MyClass: def __init__(self, stack: List["MyClass"]) -> None: pass类型注解中的类型可以是 Python 内置类型、自定义类,也可以使用typing提供的 wrapper 类进行装饰。常用注解:
# 数值类型 from numbers import Number # 可选类型,指参数可以为 None from typing import Optional def foo(var: Optional[int] = None): pass # 联合类型,指同时接受多种类型 from typing import Union def foo(var: Union[float, str]): pass from typing import Sequence # 序列类型 from typing import Iterable # 可迭代类型 from typing import Any # 任意类型 from typing import Callable # 可调用类型 from typing import List, Dict # 列表和字典的泛型类型 from typing import Tuple # 元组的特殊格式 # 虽然在 Python 3.9 中,list, tuple 和 dict 本身已支持泛型,但为了支持之前的版本 # 我们在进行类型注解时还是需要使用 List, Tuple, Dict 类型 # 另外,在对参数类型进行注解时,尽量使用 Sequence & Iterable & Mapping # List, Tuple, Dict 主要用于返回值类型注解5.3 变量类型注解
一般用于难以直接推断类型时:
# Recommend: 带类型注解的赋值 a: Foo = SomeUndecoratedFunction() a: List[int]: [1, 2, 3] # List 只支持单一类型泛型,可使用 Union b: Tuple[int, int] = (1, 2) # 长度固定为 2 c: Tuple[int, ...] = (1, 2, 3) # 变长 d: Dict[str, int] = {'a': 1, 'b': 2} # Not Recommend:行尾类型注释 # 虽然这种方式被写在了 Google 开源指南中,但这是一种为了支持 Python 2.7 版本 # 而补充的注释方式,鉴于我们只支持 Python 3, 为了风格统一,不推荐使用这种方式。 a = SomeUndecoratedFunction() # type: Foo a = [1, 2, 3] # type: List[int] b = (1, 2, 3) # type: Tuple[int, ...] c = (1, "2", 3.5) # type: Tuple[int, Text, float]5.4 自定义泛型
除了使用typing内置的List、Dict泛型,也可以利用TypeVar和Generic定义自己的泛型:
from typing import TypeVar, Generic KT = TypeVar('KT') VT = TypeVar('VT') class Mapping(Generic[KT, VT]): def __init__(self, data: Dict[KT, VT]): self._data = data def __getitem__(self, key: KT) -> VT: return self._data[key]使用上述方法定义了一个拥有泛型能力的映射类:
mapping = Mappingstr, float value: float = example['a']另外,可以利用TypeVar在函数签名中指定联动的多个类型:
from typing import TypeVar, List T = TypeVar('T') # Can be anything A = TypeVar('A', str, bytes) # Must be str or bytes def repeat(x: T, n: int) -> List[T]: """Return a list containing n references to x.""" return [x]*n def longest(x: A, y: A) -> A: """Return the longest of two strings.""" return x if len(x) >= len(y) else y5.5 mypy:类型注解检查工具
mypy 是一个 Python 静态类型检查工具。它会根据类型注解检查传参、赋值等操作是否符合注解,从而避免可能出现的 bug。考虑如下脚本test.py:
def foo(var: int) -> float: return float(var) a: str = foo('2.0') b: int = foo('3.0') # type: ignore运行mypy test.py得到:
test.py:4: error: Incompatible types in assignment (expression has type "float", variable has type "int") test.py:4: error: Argument 1 to "foo" has incompatible type "str"; expected "int" Found 2 errors in 1 file (checked 1 source file)输出分别指出了第 4 行在函数调用参数和返回值赋值两处的类型错误;第 5 行同样存在两处类型错误,但因为使用了type: ignore而被忽略——只有部分特殊情况才需要此类忽略。MMCV 在提交代码时要求补充类型注解并通过 mypy 检查(mypy 钩子已配置在 .pre-commit-config.yaml 中,排除了 tests 与 docs 目录)。
六、提交 PR 前的代码规范自查清单
结合 docs/zh_cn/community/contributing.md 与 docs/zh_cn/community/pr.md,向 MMCV 提交代码前应完成以下检查:
- pre-commit 检查:在 MMCV 目录下执行
pre-commit run --all-files,确保 flake8、yapf、isort、mdformat、docformatter、codespell、mypy 等钩子全部通过;若钩子安装被网络中断,可重复执行pre-commit run ...继续安装; - 单元测试:
pytest tests通过全量测试,至少保证修改模块的测试通过,例如pytest tests/test_utils/test_env.py;可用python -m coverage run -m pytest /path/to/test_file && python -m coverage html检查覆盖率(查看htmlcov/index.html); - 文档渲染:若修改/新增了 docstring 或文档,安装
pip install -r requirements/docs.txt后在docs/zh_cn/(或docs/en/)下执行make html,确认 reStructuredText 语法渲染无误(检查docs/zh_cn/_build/html/index.html); - 代码风格:Python 遵循 PEP 8 并遵循本文全部规范;C++ 与 CUDA 遵循 Google C++ Style Guide;
- PR 粒度与描述:一个 PR 只做一件事,粒度要细;标题格式为
[Prefix] Short description (Suffix),前缀包括[Feature](新功能)、[Fix](修 bug)、[Docs](文档)、[WIP](开发中);描述中说明修改理由、修改内容与影响,并关联相关 Issue。
遵守这套规范,既能保证你的代码与 MMCV 现有代码库高度一致,也能让代码在 Readthedocs 上正确渲染为 API 文档,惠及所有社区用户。
- 人工智能
- 计算机视觉
- 深度学习
【免费下载链接】mmcv
OpenMMLab Computer Vision Foundation
相关推荐
QUANTAXIS Python 代码规范完全指南:PEP 8、类型注解、命名规范与质量工具实战
QUANTAXIS Python 代码规范完全指南:PEP 8、类型注解、命名规范与质量工具实战 本文依据仓库文档 doc/development/code s
金融科技后端数据分析DeepChem 开发规范指南:从 pre-commit 到类型注解的完整代码质量保障体系
DeepChem 开发规范指南:从 pre commit 到类型注解的完整代码质量保障体系 本篇技术指南以 DeepChem 官方开发文档中的 Coding C
人工智能深度学习机器学习生物信息学科学计算Python 3编码规范与最佳实践:PEP 8完全解读指南
Python 3编码规范与最佳实践:PEP 8完全解读指南 想要写出专业、易读且符合行业标准的Python代码吗?掌握PEP 8编码规范是每个Python开发者
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考