【问题标题】:Python argparse: Make at least one argument requiredPython argparse:至少需要一个参数
【发布时间】:2011-10-07 01:36:21
【问题描述】:

我一直在将argparse 用于可以-process-upload 或两者兼有的Python 程序:

parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('-process', action='store_true')
parser.add_argument('-upload',  action='store_true')
args = parser.parse_args()

如果没有至少一个参数,程序是没有意义的。如何配置argparse 以强制选择至少一个参数?

更新:

跟随 cmets:用 Python 方式对具有至少一个选项的程序进行参数化的方法是什么?

【问题讨论】:

  • -x 通常是一个标志并且是可选的。如果需要,请剪掉-
  • 如果 that 选项是放?通常,选项应该是可选的,因此得名。应避免必需的选项(这也在 argparse 文档中)。
  • @AdamMatan 您提出问题已经快三年了,但我喜欢其中隐藏的挑战,并利用新解决方案可用于此类任务的优势。

标签: python argparse


【解决方案1】:
if not (args.process or args.upload):
    parser.error('No action requested, add -process or -upload')

【讨论】:

  • 如果argparse 没有内置选项,这可能是唯一的方法。
【解决方案2】:

如果不是“或两者”部分(我最初错过了这个),你可以使用这样的东西:

parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('--process', action='store_const', const='process', dest='mode')
parser.add_argument('--upload',  action='store_const', const='upload', dest='mode')
args = parser.parse_args()
if not args.mode:
    parser.error("One of --process or --upload must be given")

不过,使用subcommands 可能会更好。

【讨论】:

  • 我想他想允许--process OR --upload,而不是XOR。这可以防止同时设置两个选项。
  • +1 因为您提到了子命令。然而 - 正如有人在 cmets 中指出的那样,-x--xxx 通常是可选参数。
【解决方案3】:
args = vars(parser.parse_args())
if not any(args.values()):
    parser.error('No arguments provided.')

【讨论】:

  • +1 表示通用解决方案。也喜欢使用vars(),这对于将精心命名的选项传递给带有**的构造函数也很有用。
  • 这正是我正在做的。谢谢!
  • 当,我喜欢vars。我刚刚做了.__dict__,之前感觉很笨。
  • 很好的答案。 “vars”和“any”对我来说都是新的:-)
【解决方案4】:

如果您需要使用至少一个参数运行 python 程序,请添加一个没有具有选项前缀(默认为-或-)的参数并设置nargs=+(最小需要一个论点)。我发现这个方法的问题是,如果不指定参数,argparse会产生“参数太少”的错误,并且不会打印出帮助菜单。如果您不需要该功能,以下是如何在代码中执行此操作:

import argparse

parser = argparse.ArgumentParser(description='Your program description')
parser.add_argument('command', nargs="+", help='describe what a command is')
args = parser.parse_args()

认为,当您添加带有选项前缀的参数时,nargs 会控制整个参数解析器,而不仅仅是选项。 (我的意思是,如果你有一个--option 标志和nargs="+",那么--option 标志至少需要一个参数。如果你有optionnargs="+",它预计至少有一个参数。)

【讨论】:

  • 您可以将choices=['process','upload'] 添加到该参数中。
【解决方案5】:

需求审核

  • 使用argparse(我会忽略这个)
  • 允许调用一两个操作(至少需要一个)。
  • 尝试使用 Pythonic(我宁愿称其为“POSIX”式)

在命令行中也有一些隐含的要求:

  • 以易于理解的方式向用户解释用法
  • 选项应该是可选的
  • 允许指定标志和选项
  • 允许与其他参数(如文件名或名称)组合。

使用docopt(文件managelog.py)的示例解决方案:

"""Manage logfiles
Usage:
    managelog.py [options] process -- <logfile>...
    managelog.py [options] upload -- <logfile>...
    managelog.py [options] process upload -- <logfile>...
    managelog.py -h

Options:
    -V, --verbose      Be verbose
    -U, --user <user>  Username
    -P, --pswd <pswd>  Password

Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
"""
if __name__ == "__main__":
    from docopt import docopt
    args = docopt(__doc__)
    print args

尝试运行它:

$ python managelog.py
Usage:
    managelog.py [options] process -- <logfile>...
    managelog.py [options] upload -- <logfile>...
    managelog.py [options] process upload -- <logfile>...
    managelog.py -h

显示帮助:

$ python managelog.py -h
Manage logfiles
Usage:
    managelog.py [options] process -- <logfile>...
    managelog.py [options] upload -- <logfile>...
    managelog.py [options] process upload -- <logfile>...
    managelog.py -h

Options:
    -V, --verbose      Be verbose
    -U, --user <user>  Username
    -P, --pswd <pswd>  P    managelog.py [options] upload -- <logfile>...

Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>

并使用它:

$ python managelog.py -V -U user -P secret upload -- alfa.log beta.log
{'--': True,
 '--pswd': 'secret',
 '--user': 'user',
 '--verbose': True,
 '-h': False,
 '<logfile>': ['alfa.log', 'beta.log'],
 'process': False,
 'upload': True}

短选项short.py

还有更短的变体:

"""Manage logfiles
Usage:
    short.py [options] (process|upload)... -- <logfile>...
    short.py -h

Options:
    -V, --verbose      Be verbose
    -U, --user <user>  Username
    -P, --pswd <pswd>  Password

Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
"""
if __name__ == "__main__":
    from docopt import docopt
    args = docopt(__doc__)
    print args

用法如下:

$ python short.py -V process upload  -- alfa.log beta.log
{'--': True,
 '--pswd': None,
 '--user': None,
 '--verbose': True,
 '-h': False,
 '<logfile>': ['alfa.log', 'beta.log'],
 'process': 1,
 'upload': 1}

请注意,“进程”和“上传”键的布尔值不是布尔值,而是计数器。

事实证明,我们无法防止这些词的重复:

$ python short.py -V process process upload  -- alfa.log beta.log
{'--': True,
 '--pswd': None,
 '--user': None,
 '--verbose': True,
 '-h': False,
 '<logfile>': ['alfa.log', 'beta.log'],
 'process': 2,
 'upload': 1}

结论

设计好的命令行界面有时会很有挑战性。

基于命令行的程序有多个方面:

  • 良好的命令行设计
  • 选择/使用正确的解析器

argparse 提供了很多,但限制了可能的场景并且可能变得非常复杂。

使用docopt,事情会变得更短,同时保持可读性并提供高度的灵活性。如果您设法从字典中获取已解析的参数并手动(或通过名为 schema 的其他库)进行一些转换(到整数、打开文件..),您可能会发现 docopt 非常适合命令行解析。

【讨论】:

  • 从未听说过docopt,好建议!
  • @TonvandenHeuvel 好。我只是想确认一下,我仍然将它用作命令行界面的首选解决方案。
  • 最佳答案 evar,感谢您提供详细示例。
【解决方案6】:

将 append_const 用于操作列表,然后检查列表是否已填充:

parser.add_argument('-process', dest=actions, const="process", action='append_const')
parser.add_argument('-upload',  dest=actions, const="upload", action='append_const')

args = parser.parse_args()

if(args.actions == None):
    parser.error('Error: No actions requested')

您甚至可以直接在常量中指定方法。

def upload:
    ...

parser.add_argument('-upload',  dest=actions, const=upload, action='append_const')
args = parser.parse_args()

if(args.actions == None):
    parser.error('Error: No actions requested')

else:
    for action in args.actions:
        action()

【讨论】:

    【解决方案7】:

    对于http://bugs.python.org/issue11588,我正在探索推广mutually_exclusive_group 概念以处理此类情况的方法。

    有了这个发展argparse.py, https://github.com/hpaulj/argparse_issues/blob/nested/argparse.py 我会写:

    parser = argparse.ArgumentParser(prog='PROG', 
        description='Log archiver arguments.')
    group = parser.add_usage_group(kind='any', required=True,
        title='possible actions (at least one is required)')
    group.add_argument('-p', '--process', action='store_true')
    group.add_argument('-u', '--upload',  action='store_true')
    args = parser.parse_args()
    print(args)
    

    产生以下help:

    usage: PROG [-h] (-p | -u)
    
    Log archiver arguments.
    
    optional arguments:
      -h, --help     show this help message and exit
    
    possible actions (at least one is required):
      -p, --process
      -u, --upload
    

    这接受诸如“-u”、“-up”、“--proc --up”等输入。

    它最终运行类似于https://stackoverflow.com/a/6723066/901925 的测试,但错误消息需要更清楚:

    usage: PROG [-h] (-p | -u)
    PROG: error: some of the arguments process upload is required
    

    我想知道:

    • 参数kind='any', required=True是否足够清楚(接受任何组;至少需要一个)?

    • (-p | -u) 的用法是否明确?一个必需的互斥组产生相同的东西。有其他的符号吗?

    • 使用这样的组是否比phihag's简单测试更直观?

    【讨论】:

    • 我在此页面上找不到任何提及add_usage_groupdocs.python.org/2/library/argparse.html;你能提供一个指向它的文档的链接吗?
    • @P.MyerNore,我确实提供了一个链接 - 在这个答案的开头。这还没有投入生产。
    【解决方案8】:

    我知道这已经很老了,但是要求一个选项但禁止多个选项(XOR)的方法是这样的:

    parser = argparse.ArgumentParser()
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-process', action='store_true')
    group.add_argument('-upload',  action='store_true')
    args = parser.parse_args()
    print args
    

    输出:

    >opt.py  
    usage: multiplot.py [-h] (-process | -upload)  
    multiplot.py: error: one of the arguments -process -upload is required  
    
    >opt.py -upload  
    Namespace(process=False, upload=True)  
    
    >opt.py -process  
    Namespace(process=True, upload=False)  
    
    >opt.py -upload -process  
    usage: multiplot.py [-h] (-process | -upload)  
    multiplot.py: error: argument -process: not allowed with argument -upload  
    

    【讨论】:

    • 不幸的是,OP 不想要 XOR。它是一个或两个,但不是没有,所以你的最后一个测试用例不符合他们的要求。
    • @kdopen:受访者确实澄清说这是对原始问题的一种变体,我发现它很有用:“要求一个选项但禁止多个选项的方式”也许是堆栈的礼仪Exchange 会要求提出一个新问题。但是这里有这个答案帮助了我......
    • 我会支持这个答案的有用性,这最终正是我想要的。
    • 这篇文章没有回答最初的问题
    • 这如何回答“至少一个”的问题?
    【解决方案9】:

    最好的方法是使用python内置模块add_mutually_exclusive_group

    parser = argparse.ArgumentParser(description='Log archiver arguments.')
    group = parser.add_mutually_exclusive_group()
    group.add_argument('-process', action='store_true')
    group.add_argument('-upload',  action='store_true')
    args = parser.parse_args()
    

    如果您只想通过命令行选择一个参数,只需使用 required=True 作为组的参数

    group = parser.add_mutually_exclusive_group(required=True)
    

    【讨论】:

    • 这如何让你“至少一个” - 它不是让你“完全一个”吗?
    • 不幸的是,OP 不想要 XOR。 OP 正在寻找 OR
    • 这没有回答 OP 的问题,但它回答了我的问题,所以无论如何谢谢¯_(ツ)_/¯
    【解决方案10】:

    也许使用子解析器?

    import argparse
    
    parser = argparse.ArgumentParser(description='Log archiver arguments.')
    subparsers = parser.add_subparsers(dest='subparser_name', help='sub-command help')
    parser_process = subparsers.add_parser('process', help='Process logs')
    parser_upload = subparsers.add_parser('upload', help='Upload logs')
    args = parser.parse_args()
    
    print("Subparser: ", args.subparser_name)
    

    现在--help 显示:

    $ python /tmp/aaa.py --help
    usage: aaa.py [-h] {process,upload} ...
    
    Log archiver arguments.
    
    positional arguments:
      {process,upload}  sub-command help
        process         Process logs
        upload          Upload logs
    
    optional arguments:
      -h, --help        show this help message and exit
    $ python /tmp/aaa.py
    usage: aaa.py [-h] {process,upload} ...
    aaa.py: error: too few arguments
    $ python3 /tmp/aaa.py upload
    Subparser:  upload
    

    您也可以向这些子解析器添加其他选项。此外,除了使用 dest='subparser_name' 之外,您还可以绑定函数以在给定的子命令上直接调用(请参阅文档)。

    【讨论】:

      【解决方案11】:

      这达到了目的,这也将反映在 argparse 自动生成的 --help 输出中,这是大多数理智的程序员想要的(也适用于可选参数):

      parser.add_argument(
          'commands',
          nargs='+',                      # require at least 1
          choices=['process', 'upload'],  # restrict the choice
          help='commands to execute'
      )
      

      关于此的官方文档: https://docs.python.org/3/library/argparse.html#choices

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-16
        • 1970-01-01
        • 2017-02-08
        • 1970-01-01
        • 2018-09-18
        相关资源
        最近更新 更多