Python argparse:至少需要一个参数


92

我已经使用argparse了Python程序,可以-process-upload或两者:

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以强制至少选择一个参数?

更新:

评论之后:用Python方式至少对一个选项进行参数化的方法是什么?


9
-x通常是一个标志并且是可选的。-如果需要,请剪切。

1
您是否可以process设置默认行为(无需指定任何选项),并且upload如果设置了选项,则允许用户将其更改为?通常,选项应该是可选的,因此是名称。应避免使用必需的选项(这也在argparse 文档中)。
蒂姆·皮茨克

@AdamMatan问您问题已经快三年了,但是我喜欢隐藏在其中的挑战,并利用了可用于此类任务的新解决方案的优势。
Jan Vlcinsky 2014年

Answers:


107
if not (args.process or args.upload):
    parser.error('No action requested, add -process or -upload')

1
如果argparse没有内置选项,那可能是唯一的方法。
亚当·马坦

29
args = vars(parser.parse_args())
if not any(args.values()):
    parser.error('No arguments provided.')

3
+1为广义解决方案。也类似于的使用vars(),这对于将带有精心命名的选项传递给带有**的构造函数也很有用。
伦纳(Lenna)2013年

这正是我正在做的事情。谢谢!
brentlance

1
荡,我喜欢这样vars。我只是做过.__dict__,觉得自己很笨。
Theo Belaire 2014年

1
好答案。“ vars”和“ any”对我来说都是新手:-)
Vivek Jha

21

如果不是“或两者”部分(我最初错过了这一点),则可以使用以下方式:

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")

但是,最好改用子命令


4
我认为他想允许--processOR --upload,而不是XOR。这样可以防止同时设置两个选项。
phihag 2011年

+1是因为您提到了子命令。然而-正如有人在评论中指出-x,并--xxx通常是可选的参数。
Mac

20

我知道这已经很久了,但是需要一个选项但禁止一个以上(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  

3
不幸的是,OP不需要XOR。它要么是其中之一,要么是两者都没有,所以您的最后一个测试用例不符合他们的要求。
kdopen 2015年

2
@kdopen:受访者确实澄清了这是原始问题的一种变体,我发现这很有用:“一种方法要求一个选项,但禁止多个选项” 。但是在这里有这个答案对我有帮助...
erik.weathers 2015年

2
这个职位不回答最初的问题
马克·

2
这如何回答“至少一个”的问题?
xaxxon

2
不幸的是,OP不需要XOR。
duckman_1991 '19

8

需求审查

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

使用命令行时,还存在一些隐式要求:

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

使用docopt(file 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}

请注意,不是“ process”和“ upload”键的布尔值,而是计数器。

事实证明,我们不能阻止这些词的重复:

$ 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,很好的建议!
Ton van den Heuvel

@TonvandenHeuvel很好。我只想确认一下,我仍将其用作命令行界面的首选解决方案。
Jan Vlcinsky

最佳答案evar,谢谢您的详细示例。
jnovack '16

5

如果您需要使用至少一个参数运行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="+",那么--optionflag期望至少有一个参数。如果有option带有一个标志,则期望至少nargs="+"有一个参数。)


您可以添加choices=['process','upload']该参数。
hpaulj 2014年

5

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

通过这种开发argparse.pyhttps://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)清楚吗?必需的common_exclusive_group会产生相同的结果。有其他替代符号吗?

  • 这样的小组比phihag's简单的测试更直观吗?


add_usage_group在此页面上找不到任何提及:docs.python.org/2/library/argparse.html ; 您能否提供其文档的链接?
P. Myer Nore '18

@ P.MyerNore,我确实提供了一个链接-在此答案的开头。该产品尚未投入生产。
hpaulj

5

最好的方法是使用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)

2
这如何使您“至少一个”-不使您“完全一个”?
xaxxon

3
不幸的是,OP不需要XOR。OP寻找OR
duckman_1991 '19

这没有回答OP的问题,但是它回答了我的问题,所以还是谢谢__(ツ)_ /
¯– rosstex

2

也许使用子解析器?

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'您还可以绑定要在给定子命令上直接调用的函数(请参阅docs)。


2

这样就达到了目的,这也会在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


1

对操作列表使用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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.