Python argparse 整数条件(>=12)

31

我需要使用argparse请求一个参数的值大于等于12。

似乎没有办法在argparse中仅对给定值设置规则,而只能设置完整的接受值集合,例如choices=['rock','paper','scissors']。

我的代码是:

import sys, argparse

parser = argparse.ArgumentParser()
parser.add_argument("-b", "--bandwidth", type=int, help="target bandwidth >=12")
args = parser.parse_args()
if args.bandwidth and args.bandwidth < 12:
    print "ERROR: minimum bandwidth is 12"
    sys.exit(1)

我想知道是否有一种方法可以通过一些argparse选项直接获得这个结果。


如何通过子类化 argparse.Action 并重写 __call__ 方法来创建 >=12 规则? - Srinivas Reddy Thatiparthy
你可以写下几行代码吗? - giuspen
3
在“related”下面有一个https://dev59.com/GWYq5IYBdhLWcg3w4Ehx线程。那里的接受答案使用了`type`,并指出它是文档示例`perfect_square`的一种改编。 - hpaulj
4个回答

55

一种方法是使用自定义类型。

def bandwidth_type(x):
    x = int(x)
    if x < 12:
        raise argparse.ArgumentTypeError("Minimum bandwidth is 12")
    return x

parser.add_argument("-b", "--bandwidth", type=bandwidth_type, help="target bandwidth >= 12")
注意:我认为抛出ArgumentTypeError异常比抛出ArgumentError异常更加正确。然而,由于argparse没有将ArgumentTypeError公开作为一个类进行文档化,所以在你自己的代码中使用它可能不被认为是正确的。我喜欢的一种选择是像alecxe在他的回答中那样使用argparse.error,尽管我会使用自定义操作而不是类型函数来获得对解析器对象的访问权限。
更灵活的选项是自定义操作,这提供了对当前解析器和命名空间对象的访问权限。
class BandwidthAction(argparse.Action):

    def __call__(self, parser, namespace, values, option_string=None):
        if values < 12:
            parser.error("Minimum bandwidth for {0} is 12".format(option_string))
            #raise argparse.ArgumentError("Minimum bandwidth is 12")

        setattr(namespace, self.dest, values)

parser.add_argument("-b", "--bandwidth", action=BandwidthAction, type=int,
                     help="target bandwidth >= 12")

3
+1 自定义操作似乎是更好的选择。使用 type 看起来有些笨拙。 - alecxe
2
我认为将一个范围受限的整数集视为int的子类型是有效的,就像在数学中N(自然数)是Z(整数)的子集一样。实际上,这个操作有些过度,因为没有必要使用解析器或选项名称来对选项的值进行类型检查。 - chepner
我想考虑没有上限的情况,所以范围不是我的最佳选择。 - giuspen
1
文档中的16.4.3.6.type部分包含了一个自定义的Type示例,该示例会引发argparse.ArgumentTypeError(msg)。如果FileType无法打开一个文件,它也会引发一个ArgumentTypeErrorArgumentTypeError已被包含在__all__导入中。API文档需要进一步完善。 - hpaulj
@hpaulj 感谢您的纠正。我根据模块docstring发表了我的评论,该文档省略了“ArgumentTypeError”。显然,我没有往下仔细阅读,特别是因为__all__只在docstring下面几行。 - chepner

27

你可以尝试使用你在解释中介绍的某些内容:

import sys, argparse

parser = argparse.ArgumentParser()
parser.add_argument("-b", "--bandwidth", type=int, choices=range(12,100))
args = parser.parse_args()

例如,这里使用的是 Argparse,它会自己抛出错误信息,显示“无效选项(invalid choice)”。

4
这会对价值引入一个人为的上限。 - chepner
2
这是一个示例,我们不知道他需要的"上限"。 - user1593705
7
为什么会假设存在一个上限? - chepner
2
理论上,choices 可以与任何接受 in 运算符(具有 __contains__)的东西一起使用,包括像 range(1, 65535) 这样的东西。但正如 http://bugs.python.org/issue16418 中所讨论的那样,帮助和错误消息可能会出现问题,因为它试图列出所有值。 - hpaulj
20
这将在打印所有这些选项时破坏“-h、--help”消息。 - Achilles
显示剩余7条评论

26

您可以在不创建自定义类型或单独函数的情况下调用解析器错误。对您代码示例的简单更改就足够了:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-b", "--bandwidth", type=int, help="target bandwidth >=12")
args = parser.parse_args()
if args.bandwidth and args.bandwidth < 12:
    parser.error("Minimum bandwidth is 12")

这将导致应用程序退出并显示解析器错误:

$ python test.py --bandwidth 11 
usage: test.py [-h] [-b BANDWIDTH]
test.py: error: Minimum bandwidth is 12

这个可以完成任务,当然在子类化argparse.ArgumentParser时也可以重用该技术。请参阅相关答案 - Asclepius

1
这个怎么样?
import sys, argparse

parser = argparse.ArgumentParser()
parser.add_argument(
    "-b", "--bandwidth", 
    type=lambda x: (int(x) > 11) and int(x) or sys.exit("Minimum bandwidth is 12"),
    help="target bandwidth >=12"
)

但请注意,我没有在真正的代码中尝试过它。或者您可以按照@jonatan所写的将sys.exit更改为parser.error

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接