【问题标题】:In Python, using argparse, allow only positive integers在 Python 中,使用 argparse,只允许正整数
【发布时间】:2012-12-16 13:07:04
【问题描述】:

标题几乎概括了我希望发生的事情。

这就是我所拥有的,虽然程序不会在非正整数上崩溃,但我希望用户被告知非正整数基本上是无稽之谈。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-g", "--games", type=int, default=162,
                    help="The number of games to simulate")
args = parser.parse_args()

还有输出:

python simulate_many.py -g 20
Setting up...
Playing games...
....................

负数输出:

python simulate_many.py -g -2
Setting up...
Playing games...

现在,显然我可以添加一个 if 来确定 if args.games 是否定的,但我很好奇是否有办法将其捕获在 argparse 级别,以便利用自动使用打印。

理想情况下,它会打印类似这样的内容:

python simulate_many.py -g a
usage: simulate_many.py [-h] [-g GAMES] [-d] [-l LEAGUE]
simulate_many.py: error: argument -g/--games: invalid int value: 'a'

像这样:

python simulate_many.py -g -2
usage: simulate_many.py [-h] [-g GAMES] [-d] [-l LEAGUE]
simulate_many.py: error: argument -g/--games: invalid positive int value: '-2'

现在我正在这样做,我想我很高兴:

if args.games <= 0:
    parser.print_help()
    print "-g/--games: must be positive."
    sys.exit(1)

【问题讨论】:

    标签: python argparse


    【解决方案1】:

    这应该可以使用type。您仍然需要定义一个实际的方法来为您决定:

    def check_positive(value):
        ivalue = int(value)
        if ivalue <= 0:
            raise argparse.ArgumentTypeError("%s is an invalid positive int value" % value)
        return ivalue
    
    parser = argparse.ArgumentParser(...)
    parser.add_argument('foo', type=check_positive)
    

    这基本上只是一个改编自docs on argparse 中的perfect_square 函数的示例。

    【讨论】:

    • 你的函数可以有多个值吗?它是如何工作的?
    • 如果转换为int 失败,还会有可读的输出吗?或者你应该tryraise手动转换吗?
    • @MrZ 它会给出类似error: argument foo: invalid check_positive value: 'foo=&lt;whatever&gt;'的东西。您可以简单地在其周围添加一个 try: ... except ValueError: 以重新引发异常并提供更好的错误消息。
    【解决方案2】:

    type 将是处理条件/检查的推荐选项,如 Yuushi 的回答。

    在您的具体情况下,如果您的上限也已知,您也可以使用choices 参数:

    parser.add_argument('foo', type=int, choices=xrange(5, 10))
    

    注意:在 python 3.x 中使用 range 而不是 xrange

    【讨论】:

    • 我想这将是相当低效的,因为您将生成一个范围,然后循环通过它来验证您的输入。快速if 更快。
    • @trav1th 确实可能是这样,但这是文档中的示例用法。另外,我在回答中说过,Yuushi 的回答是最适合的。很好地提供选择。在 argparse 的情况下,每次执行都会发生一次,使用生成器 (xrange) 并且不需要额外的代码。这种权衡是可行的。由每个人决定走哪条路。
    • 为了更清楚地了解 jgritty 关于本作者答案的观点,choices=xrange(0,1000) 将导致每次使用时将 1 到 999 的整个整数列表写入您的控制台 - -help 或者如果提供了无效参数。在大多数情况下,这不是一个好的选择。
    • 如果你使用大范围的数字,我认为这不是最干净的解决方案,因为--help会输出处理过的整数,完全搞砸了--help的视图
    【解决方案3】:

    如果你的 arg 有一个可预测的最大值和最小值,那么快速而肮脏的方法是使用 choices 和一个范围

    parser.add_argument('foo', type=int, choices=xrange(0, 1000))
    

    【讨论】:

    • 缺点是可怕的输出。
    • 强调,我猜。
    • 为了更清楚地了解 jgritty 的观点,choices=xrange(0,1000) 将导致每次使用 --help 或如果提供了一个无效的参数。在大多数情况下,这不是一个好的选择。
    【解决方案4】:

    一个更简单的替代方法,尤其是在子类化argparse.ArgumentParser 时,是从parse_args 方法内部启动验证。

    在这样的子类中:

    def parse_args(self, args=None, namespace=None):
        """Parse and validate args."""
        namespace = super().parse_args(args, namespace)
        if namespace.games <= 0:
             raise self.error('The number of games must be a positive integer.')
        return namespace
    

    这种技术可能不像自定义可调用对象那么酷,但它确实可以完成这项工作。


    关于ArgumentParser.error(message)

    此方法打印一条使用消息,包括标准错误消息,并以状态码 2 终止程序。


    信用:answer by jonatan

    【讨论】:

    • 或者至少,将print "-g/--games: must be positive."; sys.exit(1) 替换为parser.error("-g/--games: must be positive.")(用法如jonatan's answer。)
    【解决方案5】:

    如果有人(比如我)在 Google 搜索中遇到这个问题,这里有一个示例,说明如何使用模块化方法巧妙地解决更普遍的问题,即允许 argparse 整数只在指定范围内 /em>:

    # Custom argparse type representing a bounded int
    class IntRange:
    
        def __init__(self, imin=None, imax=None):
            self.imin = imin
            self.imax = imax
    
        def __call__(self, arg):
            try:
                value = int(arg)
            except ValueError:
                raise self.exception()
            if (self.imin is not None and value < self.imin) or (self.imax is not None and value > self.imax):
                raise self.exception()
            return value
    
        def exception(self):
            if self.imin is not None and self.imax is not None:
                return argparse.ArgumentTypeError(f"Must be an integer in the range [{self.imin}, {self.imax}]")
            elif self.imin is not None:
                return argparse.ArgumentTypeError(f"Must be an integer >= {self.imin}")
            elif self.imax is not None:
                return argparse.ArgumentTypeError(f"Must be an integer <= {self.imax}")
            else:
                return argparse.ArgumentTypeError("Must be an integer")
    

    这允许您执行以下操作:

    parser = argparse.ArgumentParser(...)
    parser.add_argument('foo', type=IntRange(1))     # Must have foo >= 1
    parser.add_argument('bar', type=IntRange(1, 7))  # Must have 1 <= bar <= 7
    

    变量 foo 现在只允许 正整数,就像 OP 要求的那样。

    请注意,除了上述形式之外,IntRange 也可以是最大值:

    parser.add_argument('other', type=IntRange(imax=10))  # Must have other <= 10
    

    【讨论】:

      【解决方案6】:

      根据 Yuushi 的回答,您还可以定义一个简单的辅助函数,该函数可以检查一个数字对于各种数字类型是否为正:

      def positive(numeric_type):
          def require_positive(value):
              number = numeric_type(value)
              if number <= 0:
                  raise ArgumentTypeError(f"Number {value} must be positive.")
              return number
      
          return require_positive
      

      辅助函数可用于注释任何数字参数类型,如下所示:

      parser = argparse.ArgumentParser(...)
      parser.add_argument("positive-integer", type=positive(int))
      parser.add_argument("positive-float", type=positive(float))
      

      【讨论】:

        猜你喜欢
        • 2021-03-06
        • 2015-02-26
        • 1970-01-01
        • 1970-01-01
        • 2019-12-16
        • 2018-07-04
        • 2020-11-29
        • 2016-06-30
        • 2014-02-12
        相关资源
        最近更新 更多