Python中使用argparse实现条件命令行参数

45
我希望有一个程序,能够使用--action=标志,并提供dumpupload两个有效选项,默认为upload。仅当选择dump时,我希望还有一个--dump-format=选项。是否可以使用argparse实现这一点,或者我需要接受所有参数并自己处理逻辑?

从美学角度来看,像 --action=dump-csv--action=dump-some-other-format 这样做是否是可行的选择?这将完全缓解“必需选项”的问题。 - dcrosta
@dcrosta,这显然可以实现,但我不喜欢那种方式,我觉得它很笨重。 - Alex Gaynor
好的,我只是想确保你已经涵盖了显而易见的基础知识。 - dcrosta
他需要默认为上传 -- parser.add_argument('--action', choices=['upload', 'dump'], default='dump'),但我没有考虑到parser.error。 - Iman
5个回答

81

使用argparse模块可以避免自己编写必需参数检查的繁琐操作。下面的示例使用“子解析器”或“子命令”。我已经为“dump”和“format”实现了一个子解析器。

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('file', help='The file you want to act on.')
subparsers = parser.add_subparsers(dest='subcommand')
subparsers.required = True  # required since 3.7

#  subparser for dump
parser_dump = subparsers.add_parser('dump')
# add a required argument
parser_dump.add_argument(
    'format',
    choices=['csv', 'json'],
    help='Dump the file in this format.')

#  subparser for upload
parser_upload = subparsers.add_parser('upload')
# add a required argument
parser_upload.add_argument(
    'server',
    choices=['amazon', 'imgur'],
    help='Upload the file to this service.')

args = parser.parse_args()
print args
if args.subcommand == 'dump':
    print 'I will now dump "%s" in the %s format' % (args.file, args.format)
if args.subcommand == 'upload':
    print 'I will now upload "%s" to %s' % (args.file, args.server)

这在命令行上看起来像这样:

$ python ap.py 
usage: ap.py [-h] file {upload,dump} ...
ap.py: error: too few arguments

$ python ap.py tmp.txt 
usage: ap.py [-h] file {upload,dump} ...
ap.py: error: too few arguments

上传:

$ python ap.py tmp.txt upload
usage: ap.py file upload [-h] {amazon,imgur}
ap.py file upload: error: too few arguments

$ python ap.py tmp.txt upload amazo
usage: ap.py file upload [-h] {amazon,imgur}
ap.py file upload: error: argument server: invalid choice: 'amazo' (choose from 'amazon', 'imgur')

$ python ap.py tmp.txt upload amazon
Namespace(file='tmp.txt', server='amazon', subcommand='upload')
I will now upload "tmp.txt" to amazon

$ python ap.py tmp.txt upload imgur
Namespace(file='tmp.txt', server='imgur', subcommand='upload')
I will now upload "tmp.txt" to imgur

转储:

$ python ap.py tmp.txt dump
usage: ap.py file dump [-h] {csv,json}
ap.py file dump: error: too few arguments

$ python ap.py tmp.txt dump csv
Namespace(file='tmp.txt', format='csv', subcommand='dump')
I will now dump "tmp.txt" in the csv format

$ python ap.py tmp.txt dump json
Namespace(file='tmp.txt', format='json', subcommand='dump')
I will now dump "tmp.txt" in the json format

更多信息:ArgumentParser.add_subparsers()


命令行参数的设计与所要求的不同。 - debashish.ghosh
Python的print不是需要()吗?为什么上面的例子中没有使用呢?当我运行上面的例子时会出错,因为print告诉我语法应该是print('something')而不是print 'something'。这是特定于环境的怪异行为吗?(我在Windows上运行) - undefined
1
@skeetastax print在Python 2中是这样工作的,这个答案是2012年时Python 2还很常见的时候给出的。 - undefined

43

另一种处理该问题的方式是使用子命令(如git)以"action"作为第一个参数:

script dump --dump-format="foo"
script upload

一个限制是 argparse 不允许多个 subparsers - Liang
1
这是错误的。您可以添加任意数量的子解析器。请参见答案中引用的子命令链接。还请参阅Niels Bom的答案 - Adrian W
你是不是指的是子命令的嵌套?例如 dump dumpsubcommand1dump subcommand2upload subcommand3 等等? - Adrian W

22
你可以使用 parser.error
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--action', choices=['upload', 'dump'], default='dump')
parser.add_argument('--dump-format')
args = parser.parse_args()
if args.action != 'dump' and args.dump_format:
    parser.error('--dump-format can only be set when --action=dump.')

12
这只是描述“argparse方式”报告错误的方法,逻辑是手动实现的。这不是条件性要求参数的“argparse”方式。 - Niels Bom
1
如果不是只有“dump”和“format”,而是还有5个选项,每个选项都有自己的必需或非必需参数集,那么检查必需性的方式很快就会变得不清楚。 - Niels Bom
此外,像 argparse.FileType 这样的东西会出现问题。对于某些选定的操作,当不需要 FileType 参数时,文件不存在会导致错误的出现。 - Joshua Chia

6

这取决于您所谓的“自己处理所有逻辑”的定义。您仍然可以使用argparse,并通过最小的努力添加转储选项,而无需使用子命令:

from argparse import ArgumentParser
from sys import argv

parser = ArgumentParser()
action_choices = ['upload', 'dump']
parser.add_argument('--action', choices=action_choices, default=action_choices[1])
parser.add_argument('--dump-format', required=(action_choices[1] in argv))

这样做,如果未选择转储操作,argparse将不关心转储格式。

这是针对特定用例的聪明、优雅的解决方案。 - drkostas

1

试一试。

假设你有一个工具,可以列出、添加和删除以下结构的表中的记录:

id sitekey response status
1 123456 valid a
2 234567 invalid
3 345678 invalid c
4 456789 valid d

而且你想要以下操作和参数:

  • 列表
    • 从:可选
    • 到:可选
    • 短响应:可选
  • 添加
    • 站点密钥:必填
    • 响应:必填
    • 状态:可选
  • 移除
    • id:必填

那么,你可以使用类似下面的代码:

import argparse
import sys

operation_choices = ['list', 'add', 'remove']
parser = argparse.ArgumentParser()
parser.add_argument("--operation",
                    choices = operation_choices,
                    default = operation_choices[0],
                    help = "Your help!",
                    required = True)

# list operation subarguments
if True in list(map(lambda x: operation_choices[0] in x, sys.argv)):
    parser.add_argument("--from",
                        type = int,
                        default = 1,
                        help = "Your help!",
                        required = False)
    parser.add_argument("--to",
                        type = int,
                        help = "Your help!",
                        required = False)
    parser.add_argument("--short-response",
                        type = bool,
                        default = True,
                        help = "Your help!",
                        required = False)

# add operation subarguments
if True in list(map(lambda x: operation_choices[1] in x, sys.argv)):
    parser.add_argument("--sitekey",
                        type = str,
                        help = "Your help!",
                        required = True)
    parser.add_argument("--response",
                        type = str,
                        help = "Your help!",
                        required = True)
    parser.add_argument("--status",
                        type = str,
                        help = "Your help!",
                        required = False)

# remove operation subarguments
if True in list(map(lambda x: operation_choices[2] in x, sys.argv)):
    parser.add_argument("--id",
                        type = int,
                        help = "Your help!",
                        required = True)

args = parser.parse_args()

# Your operations...

因此,当您运行:

$ python tool.py --operation=list

This run, no required arguments

$ python tool.py --operation=add

usage: tool.py [-h] --operation {list,add,remove} --sitekey SITEKEY --response RESPONSE [--status STATUS]
tool.py: error: the following arguments are required: --sitekey, --response

$ python tool.py --operation=remove

usage: tool.py [-h] --operation {list,add,remove} --id ID
tool.py: error: the following arguments are required: --id

$ python tool.py --help

usage: tool.py [-h] --operation {list,add,remove}

options:
  -h, --help            show this help message and exit
  --operation {list,add,remove}
                        Your help!

$ python tool.py --operation=list --help

usage: tool.py [-h] --operation {list,add,remove} [--from FROM] [--to TO] [--short-response SHORT_RESPONSE]

options:
  -h, --help            show this help message and exit
  --operation {list,add,remove}
                        Your help!
  --from FROM           Your help!
  --to TO               Your help!
  --short-response SHORT_RESPONSE
                        Your help!

$ python tool.py --operation=add --help

usage: tool.py [-h] --operation {list,add,remove} --sitekey SITEKEY --response RESPONSE [--status STATUS]

options:
  -h, --help            show this help message and exit
  --operation {list,add,remove}
                        Your help!
  --sitekey SITEKEY     Your help!
  --response RESPONSE   Your help!
  --status STATUS       Your help!

$ python tool.py --operation=remove --help

usage: tool.py [-h] --operation {list,add,remove} --id ID

options:
  -h, --help            show this help message and exit
  --operation {list,add,remove}
                        Your help!
  --id ID               Your help!

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