【问题标题】:Python subparser parsing with nargs='+'使用 nargs='+' 解析 Python 子解析器
【发布时间】:2016-03-10 06:53:37
【问题描述】:

我必须用这样的命令行参数制作一个解析器

python function.py -v 1 name1 -d abc xyz foo bar name2 -i 3 -p abc xyz

我这样做了:

import argparse
parser = argparse.ArgumentParser(prog='PROG')
parser.add_argument('-v', type=int, help='number')

subparsers = parser.add_subparsers(help='sub-command help', dest="command")
parser_a = subparsers.add_parser('name1', help="name1 help")
parser_a.add_argument("-d", help="list", nargs="+")

parser_b = subparsers.add_parser('name2', help="name2 help")
parser_b.add_argument("-p", help="list2", nargs='+')
parser_b.add_argument("-i", help="number inside name2", type=int)

当我运行这个时:

parser.parse_args('-v 1 name1 -d abc xyz foo bar name2 -i 3 -p abc xyz'.split())

结果:

usage: PROG [-h] [-v V] {name1,name2} ...
PROG: error: unrecognized arguments: -i 3 -p abc xyz

运行时:

parser.parse_args('-v 1 name1 -d abc xyz foo bar name2'.split())

结果如下:

Namespace(command='name1', d=['abc', 'xyz', 'foo', 'bar', 'name2'], v=1)

我希望它是这样的:

Namespace(command='name1', d=['abc', 'xyz', 'foo', 'bar'], command='name2', p=['abc', 'xyz'], i=3, v=1)

我该怎么做?

【问题讨论】:

  • 两个问题:一次只能调用一个子解析器(作为主脚本的第一个位置参数),即使您可以调用多个子解析器,也无法知道参数在哪里-d 选项结束。 name2 是位置参数还是 -d 的参数?
  • name2 应该是一个位置参数。如果这种方式不可行,有没有其他方法可以做到这一点?
  • 您可以使用此解决方法:blog.elsdoerfer.name/2010/08/08/…

标签: python argparse


【解决方案1】:

如你所见

In [24]: parser.parse_args('-v 1 name1 -d abc xyz foo bar name2'.split())
Out[24]: Namespace(command='name1', d=['abc', 'xyz', 'foo', 'bar', 'name2'], v=1)

name2 被视为parser_a -d 参数的参数之一

不接受其余的“-i 3 -p abc xyz”,因为-i 被理解为可选标志。

实际上你正在运行:

In [28]: parser_a.parse_args('-d abc xyz foo bar name3 -i 3 -p abc xyz'.split())
usage: PROG name1 [-h] [-d D [D ...]]
PROG name1: error: unrecognized arguments: -i 3 -p abc xyz

主解析器有一个位置参数,接受 2 个选项,name1name2。当遇到name1 时,它会将其余参数传递给parser_a

最终结果是argparse 只接受一个子命令。

我在之前的 SO 问题中讨论了一些解决方法。侧边栏找到了一个:

Python argparser repeat subparse

(这可能足以将此问题标记为重复问题)。


如果我向parser_a 添加位置;并使用-- 分隔barname2(意思是,后面的都是位置)

In [32]: parser_a.add_argument('extra',nargs='*')
Out[32]: _StoreAction(option_strings=[], dest='extra', nargs='*', const=None, default=None, type=None, choices=None, help=None, metavar=None)

In [33]: parser.parse_args('-v 1 name1 -d abc xyz foo bar -- name2 -i 3 -p abc xyz'.split())
Out[33]: Namespace(command='name1', d=['abc', 'xyz', 'foo', 'bar'], extra=['name2', '-i', '3', '-p', 'abc', 'xyz'], v=1)

然后我可以将extra 字符串(减去name2)传递给parser_b

In [34]: args=_
In [36]: parser_b.parse_args(args.extra[1:])
Out[36]: Namespace(i=3, p=['abc', 'xyz'])

诚然,这是一个复杂的解决方案,但我几年前提出的解决方案也好不到哪里去。希望它有助于澄清发生了什么。


您真的需要一次调用 2 个子命令吗? argparse 如果可以的话会简单很多:

python function.py -v 1 name1 -d abc xyz foo bar
python function.py -v 1 name2 -i 3 -p abc xyz

换句话说,调用脚本两次。如果对function.py 的每次调用都执行一个独立的自包含操作,那么这很容易——两个调用之间的任何连接都包含在共享文件或数据库中。这就是经常使用子命令的方式。尝试将多个子命令放在一个调用中并不能节省输入。

【讨论】:

  • 其实原来的命令是:python function.py -v 1 name1 name2 其中name1,name2是要运行的子模块。现在我希望那些也接受命令行参数。
【解决方案2】:

我认为你做错了什么。 首先你应该传递位置参数,然后是关键字参数,例如

python myapp.py arg1 arg2 --kwarg1=1 --kwarg2 val1 --kwarg2 val2 

【讨论】:

  • argparse 更灵活;它可以处理混合位置和标记的位置。但在这种情况下,他在引入自己的解析约束时也使用了subparsers
  • @hpaulj 我的意思是在这种情况下子解析器是多余的
猜你喜欢
  • 2022-11-19
  • 2018-12-31
  • 1970-01-01
  • 2015-12-11
  • 1970-01-01
  • 1970-01-01
  • 2017-03-31
  • 1970-01-01
  • 2012-08-27
相关资源
最近更新 更多