8

我需要实现程序接受子命令的命令行界面。

例如,如果程序名为“foo”,则 CLI 看起来像

foo cmd1 <cmd1-options>
foo cmd2
foo cmd3 <cmd3-options>

cmd1并且cmd3必须与它们的至少一个选项一起使用,并且三个cmd*参数始终是互斥的。

我正在尝试在 argparse 中使用子解析器,但目前没有成功。问题在于cmd2,它没有参数:

如果我尝试添加不带参数的子解析器条目,则返回的命名空间parse_args将不包含任何告诉我选择了此选项的信息(请参见下面的示例)。如果我尝试将cmd2作为参数添加到parser(而不是子解析器),那么 argparse 将期望该cmd2参数后跟任何子解析器参数。

有没有一种简单的方法来实现这一点argparse?用例应该很常见……</p>

以下是我迄今为止所尝试的更接近我需要的内容:

parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='Functions')
parser_1 = subparsers.add_parser('cmd1', help='...')
parser_1.add_argument('cmd1_option1', type=str, help='...')

parser_2 = subparsers.add_parser(cmd2, help='...')

parser_3 = subparsers.add_parser('cmd3', help='...')
parser_3.add_argument('cmd3_options', type=int, help='...')

args = parser.parse_args()
4

2 回答 2

9

首先,子解析器永远不会插入到命名空间中。在您发布的示例中,如果您尝试将脚本运行为:

$python3 test_args.py cmd1 1
Namespace(cmd1_option1='1')

其中test_args.py包含您提供的代码(import argparse开头和print(args)结尾都有 )。

请注意,cmd1仅提及其论点。这是设计使然

正如评论中所指出的,您可以添加将dest参数传递给add_subparsers调用的信息。

处理这些情况的常用方法是使用set_defaultssubparsers 的方法:

import argparse
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(help='Functions')
parser_1 = subparsers.add_parser('cmd1', help='...')
parser_1.add_argument('cmd1_option1', type=str, help='...')
parser_1.set_defaults(parser1=True)

parser_2 = subparsers.add_parser('cmd2', help='...')
parser_2.set_defaults(parser2=True)

parser_3 = subparsers.add_parser('cmd3', help='...')
parser_3.add_argument('cmd3_options', type=int, help='...')
parser_3.set_defaults(parser_3=True)

args = parser.parse_args()
print(args)

结果是:

$python3 test_args.py cmd1 1
Namespace(cmd1_option1='1', parser1=True)
$python3 test_args.py cmd2
Namespace(parser2=True)

通常,大多数情况下,不同的子解析器会以完全不同的方式处理参数。通常的模式是具有不同的功能来运行不同的命令并用于set_defaults设置func属性。当您解析参数时,您只需调用该可调用对象:

subparsers = parser.add_subparsers()
parser_1 = subparsers.add_parser(...)
parser_1.set_defaults(func=do_command_one)

parser_k = subparsers.add_parser(...)
parser_k.set_defaults(func=do_command_k)

args = parser.parse_args()
if args.func:
    args.func(args)
于 2014-10-06T14:01:22.633 回答
2

Namespace如果给命令add_subparsers一个dest.

从文档中:

但是,如果需要检查被调用的子解析器的名称,add_subparsers() 调用的 dest 关键字参数将起作用:

>>> parser = argparse.ArgumentParser()
>>> subparsers = parser.add_subparsers(dest='subparser_name')
>>> subparser1 = subparsers.add_parser('1')
>>> subparser1.add_argument('-x')
>>> subparser2 = subparsers.add_parser('2')
>>> subparser2.add_argument('y')
>>> parser.parse_args(['2', 'frobble'])
Namespace(subparser_name='2', y='frobble')

默认情况下destargparse.SUPPRESS,它subparsers不会将名称添加到namespace.

于 2014-10-06T23:03:33.807 回答