为 argparse 参数提供类型提示的更好选择,而不是自定义命名空间

Ola*_*laf 5 python type-hinting argparse mypy

我正在尝试使用 mypy 来检查我的程序。该程序使用 argparse 来解析命令行参数。我想为命令行参数添加类型提示。

import argparse
import typing


# define example types and a function that uses them
Seconds = typing.NewType("Seconds", float)
Minutes = typing.NewType("Minutes", float)

def sec_to_min(s: Seconds) -> Minutes:
    return Minutes(s / 60)

# specify arguments and parse them
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--time", default=1., type=float,
                    help="time in seconds")
args = parser.parse_args()

try:
    # mypy reveals type Any
    reveal_type(args.time)
except NameError:
    pass


# (1) passes type check
seconds: Seconds = args.time
# (2) passes type check
sec_to_min(args.time)
Run Code Online (Sandbox Code Playgroud)

我希望 mypy 识别args.timeSeconds. 当前代码应该识别args.time为浮点数并抱怨,因为也将浮点数传递给了sec_to_min抱怨。

我尝试更改to的type参数。这没有效果,mypy 仍然标识为. 正如这里提到的,我还尝试提供自定义 NameSpace。这将 的显示类型更改为。我想避免这种情况,因为我必须为每个参数复制名称,并且默认值 from被自定义命名空间覆盖。add_argumenttype=lambda x: Seconds(float(x))args.timeany
args.timeSecondsadd_argument

class TypedNameSpace(argparse.Namespace):
    def __init__(self, *args, **kwargs):
        self.time: Seconds = Seconds(0.)
        super(TypedNameSpace, self).__init__(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

blh*_*ing 1

创建自定义命名空间是正确的方法。您不必为名称指定默认值。只需在类级别键入暗示每个名称就足以让 mypy 在实例级别识别属性的类型:

class TimeNamespace(argparse.Namespace):
    time: Seconds

...

args = parser.parse_args(namespace=TimeNamespace())

# mypy reveals type "__main__.Seconds"
reveal_type(args.time)
Run Code Online (Sandbox Code Playgroud)

通过 mypy 的代码演示:https://mypy-play.net/? mypy=latest&python=3.12&gist=4028679c0e89a2c2f609aa2592486e0c

1.成功使用for--time和输出的默认值的代码演示0.016666666666666666https://ideone.com/8KEGgb