【问题标题】:How to create an argument that is optional?如何创建一个可选的参数?
【发布时间】:2013-07-16 10:41:41
【问题描述】:

除了用户必须使用script.py --file c:/stuff/file.txt之外,有没有办法让用户有选择地使用--file?因此,它看起来像 script.py c:/stuff/file.txt,但解析器仍然知道用户指的是 --file 参数(因为它是隐含的)。

【问题讨论】:

  • 你使用什么参数解析器?例如,argparse 对此提供了支持。
  • argv 就是这个概念。
  • 例子,因为我已经用 argparse 试过了。
  • 我可以看一个 TerribleSwiftTomato 的例子吗?
  • argparse 支持位置参数,这正是您要寻找的。​​span>

标签: python command-line-arguments argparse


【解决方案1】:

在您的设计中,存在一个主要的歧义(这已经是一个合理的解释,为什么它没有被 argparse 实现):

  • 如果在这样的“双模式”参数之后还有更多位置参数(例如,foo/--foobar),应该在像--foo=foo bar 这样的命令行中为哪个位置参数分配位置参数?如果有多个“双模式”参数,这会变得更加混乱。

在我两年前编写的使用“双模式”参数的脚本中,我完全禁止此类输入,要求“位置模式”参数(如果有)首先出现,然后是“命名模式”参数。

脚本在 Perl 中,在“传递”模式下使用 Perl 的 Getopt::Long 解析其他选项后,我使用自定义逻辑来实现这一点(它会传递任何无法识别的参数)。

所以我建议你也这样做,使用ArgumentParser.parse_known_args()

【讨论】:

    【解决方案2】:

    试试这个

    import argparse
    
    class DoNotReplaceAction(argparse.Action):
        def __call__(self, parser, namespace, values, option_string=None):
            if not getattr(namespace, self.dest):
                setattr(namespace, self.dest, values)
    
    parser = argparse.ArgumentParser(description="This is an example.")
    parser.add_argument('file', nargs='?', default='', help='specifies a file.', action=DoNotReplaceAction)
    parser.add_argument('--file', help='specifies a file.')
    
    args = parser.parse_args()
    # check for file argument
    if not args.file:
        raise Exception('Missing "file" argument')
    

    查看帮助信息。所有参数都是可选的

    usage: test.py [-h] [--file FILE] [file]
    
    This is an example.
    
    positional arguments:
      file         specifies a file.
    
    optional arguments:
      -h, --help   show this help message and exit
      --file FILE  specifies a file.
    

    需要注意的一点是位置file 将覆盖可选--file 并将args.file 设置为默认''。为了克服这个问题,我使用自定义action 定位file。它禁止覆盖已设置的属性。

    要注意的另一件事是,您可以指定默认值,而不是提高 Exception

    【讨论】:

    • 为了强制要求,请在您的示例中添加if not args.file: 子句。
    • @BrianCain 好点。加上指定默认值的节点。
    • @J.F.Sebastian:我怀疑argparse 在这里失败了,因为--filefile 的默认dest 都是创建的Namespace.file 属性。添加dest= 可以让它工作,但它基本上回到了我的建议,除了nargs 的区别('?''*')。
    • @J.F.Sebastian 我已经测试了我的解决方案。只需添加print args.file 并运行类似./test.py some.txt --file some.xtx 的脚本,您应该会看到some.xtx。我的python版本是2.7.3
    • @twil:点击我之前评论中的链接。它显示了问题。 Here's a test where you can pass your own input./test.py some.txt --file some.xtx 是无效输入:应该给出 some.txt--file some.xtx,而不是两者。
    【解决方案3】:

    要接受--file FILE 或只接受FILE,您可以使用mutually_exclusive_group()

    import argparse
    
    parser = argparse.ArgumentParser(prog='script',
                                     description="This is an example.",
                                     usage='%(prog)s [-h] (--file FILE | FILE)')
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('positional_file', nargs='?', help='specifies a file.')
    group.add_argument('--file', help='specifies a file.')
    
    args = parser.parse_args()
    print(args)
    filename = args.positional_file if args.file is None else args.file
    

    Examples

    ['abc'] -> Namespace(file=None, positional_file='abc')
    
    ['--file', 'abc'] -> Namespace(file='abc', positional_file=None)
    
    ['--file', 'abc', 'def'] -> usage: script [-h] (--file FILE | FILE)
    script: error: argument positional_file: not allowed with argument --file
    
    [] -> usage: script [-h] (--file FILE | FILE)
    script: error: one of the arguments positional_file --file is required
    

    【讨论】:

    • 不错。我应该在我的一个旧脚本中使用它,这些脚本变得互斥(但都是可选的)-i 和 -n 参数。 :-)
    【解决方案4】:

    如果我可以将您的问题改写为答案,您需要一个脚本,当它运行时:

    • script blahblah 视为要打开的文件名
    • script --file blahblah 视为要打开的文件名
    • script --file blah eggsblah 视为要打开的文件名,而eggs ... 如何?
    • script blah eggs 对待 blah ... 不同吗?怎么样?

    无论如何,我还是从 argparse 开始:

    #! /usr/bin/env python
    
    import argparse
    
    parser = argparse.ArgumentParser(description='script to morgle blahs')
    parser.add_argument('--file', help='specify file name to be opened')
    parser.add_argument('args', metavar='FILE', nargs='*')
    args = parser.parse_args()
    
    print args
    

    此时运行./script.py -h 会产生:

    usage: script.py [-h] [--file FILE] [FILE [FILE ...]]
    
    script to morgle blahs
    
    positional arguments:
      FILE
    
    optional arguments:
      -h, --help   show this help message and exit
      --file FILE  specify file name to be opened
    

    额外的运行:

    $ ./script.py
    Namespace(args=[], file=None)
    $ ./script.py blah
    Namespace(args=['blah'], file=None)
    $ ./script.py --file blah eggs
    Namespace(args=['eggs'], file='blah')
    $ ./script.py blah eggs
    Namespace(args=['blah', 'eggs'], file=None)
    

    所以,现在您可以测试args.file 是否为None(不是--file),而不是简单的print args,然后检查args.args,如果args.file不是 None,您仍然可以查看args.args

    如果在某个时候,您在自己的代码中确定某些参数组合是错误/无效的,您可以调用 parser.error,例如:

    if args.file is not None and len(args.args) > 0:
        parser.error('use [--file] <filename>, not --file <filename1> <filename2>')
    if args.file is None and len(args.args) != 1:
        parser.error('use [--file] <filename>')
    

    只需要一个参数,无论前面是否有 --file 字符串。

    【讨论】:

      【解决方案5】:

      您可以在argparse 中使用required=True 标志:

      import argparse
      
      parser = argparse.ArgumentParser(description="Describe stuff.")
      parser.add_argument('--foo', required=True, help='bar')
      

      但是,正如this documentation says,将选项设为必需被认为是错误的形式,因为用户希望选项是可选的。

      您可以改为定义所需参数,然后将可选的--foo 标志存储到此所需参数。这可能会导致解析器抛出异常,因为它可能认为您只是忽略了所需的参数。

      import argparse
      
      parser = argparse.ArgumentParser(description="Will this work?")
      parser.add_argument('bar', help="required argument")
      parser.add_argument('--foo', required=False, help="kind of required argument", dest='bar')
      

      我认为最好的答案是没有一个需要的标志。只需将变量设为必需并为其定义默认值,以防您需要在程序中使用某些东西但默认值以某种方式适用:

      import argparse
      
      parser = argparse.ArgumentParser(description="Other option.")
      parser.add_argument('bar', default='value', help="required argument")
      

      【讨论】:

        猜你喜欢
        • 2011-10-30
        • 1970-01-01
        • 2021-06-20
        • 1970-01-01
        • 2020-10-10
        • 1970-01-01
        • 2010-09-07
        • 2011-06-20
        • 1970-01-01
        相关资源
        最近更新 更多