Python argparse 使用nargs > 1时的类型和选择限制

23 投票
4 回答
15314 浏览
提问于 2025-04-17 08:57

标题基本上已经说明了一切。如果我有多个参数(nargs大于1),有没有办法对每个解析出来的参数设置一些限制(比如选择或类型)呢?

这里有一些示例代码:

parser = argparse.ArgumentParser()
parser.add_argument('-c', '--credits', nargs=2,
    help='number of credits required for a subject')

对于-c这个参数,我需要指定一个科目和所需的学分数。科目应该限制在一个预定义的科目列表中,而所需的学分数应该是一个浮点数。

我可能可以通过使用子解析器来实现这个功能,但现在这个参数已经是一个子命令的一部分了,所以我不想让事情变得更复杂。

4 个回答

0

一个调用 Action 类的地方只会捕捉到一个参数错误。

https://github.com/python/cpython/blob/3.8/Lib/argparse.py#L1805

如果你希望调用者能够捕捉到一个异常,你应该在你自定义的动作中这样抛出异常。

raise ArgumentError(self, '无效的主题 {s!r}'.format(s=subject))

22

顺便提一下,因为这个问题在搜索“argparse nargs choices”时会出现:

如果你的参数需要不同类型的验证,也就是说,第一个参数的类型(这里是有限的主题类型)和第二个参数的类型(这里是浮点数)不一样,那么就需要自定义一个动作。

如果你只需要验证相同类型的参数,那直接把 nargschoices 结合在一起就可以了。例如:

parser.add_argument(
    "--list-of-xs-or-ys",
    nargs="*",
    choices=["x", "y"],
)

这样做的话,用户可以输入类似 --list-of-xs-or-ys x y x y 的内容,但如果用户输入其他的东西,比如不是 xy 的内容,就会报错。

23

你可以通过一个自定义的动作来验证它:

import argparse
import collections


class ValidateCredits(argparse.Action):
    def __call__(self, parser, args, values, option_string=None):
        # print '{n} {v} {o}'.format(n=args, v=values, o=option_string)
        valid_subjects = ('foo', 'bar')
        subject, credits = values
        if subject not in valid_subjects:
            raise ValueError('invalid subject {s!r}'.format(s=subject))
        credits = float(credits)
        Credits = collections.namedtuple('Credits', 'subject required')
        setattr(args, self.dest, Credits(subject, credits))

parser = argparse.ArgumentParser()
parser.add_argument('-c', '--credits', nargs=2, action=ValidateCredits,
                    help='subject followed by number of credits required',
                    metavar=('SUBJECT', 'CREDITS')
                    )
args = parser.parse_args()
print(args)
print(args.credits.subject)
print(args.credits.required)

比如说,

% test.py -c foo 2
Namespace(credits=Credits(subject='foo', required=2.0))
foo
2.0
% test.py -c baz 2
ValueError: invalid subject 'baz'
% test.py -c foo bar
ValueError: could not convert string to float: bar

撰写回答