Python中使用argparse处理多个位置参数
我正在尝试使用argparse这个工具来解析我正在开发的程序的命令行参数。简单来说,我需要支持多个位置参数,这些参数要和可选参数混合在一起,但我在这种情况下无法让argparse正常工作。在实际的程序中,我使用了一个自定义的操作(我需要在每次找到一个位置参数时存储当前的命名空间快照),但我遇到的问题可以用append
这个操作来复现:
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-a', action='store_true')
>>> parser.add_argument('-b', action='store_true')
>>> parser.add_argument('input', action='append')
>>> parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
usage: ipython [-h] [-a] [-b] input
ipython: error: unrecognized arguments: filetwo filethree
我希望最终得到的命名空间是(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])
,但我不知道该怎么做——如果真的可以的话。我在文档或Google上没有找到任何说明这是否可能的信息,虽然很有可能(甚至可能是肯定的)我忽略了什么。有没有人能给点建议?
4 个回答
‘append’这个操作在有可选项的时候更有意义:
parser.add_argument('-i', '--input',action='append')
parser.parse_args(['-i','fileone', '-a', '-i','filetwo', '-b', '-i','filethree'])
你可以把可选项和位置参数交替使用(比如‘input1 -a input2 -b input3’),但你不能在一个多项位置参数中交替使用可选项。不过,你可以通过两步解析来实现这个功能。
import argparse
parser1 = argparse.ArgumentParser()
parser1.add_argument('-a', action='store_true')
parser1.add_argument('-b', action='store_true')
parser2 = argparse.ArgumentParser()
parser2.add_argument('input', nargs='*')
ns, rest = parser1.parse_known_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
# Namespace(a=True, b=True), ['fileone', 'filetwo', 'filethree']
ns = parser2.parse_args(rest, ns)
# Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])
http://bugs.python.org/issue14191 是一个提议的补丁,它可以通过一次调用来实现这个功能:
parser.parse_intermixed_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
你不能把开关选项(比如 -a
和 -b
)和位置参数(比如 fileone、filetwo 和 filethree)混在一起使用。开关选项必须放在位置参数之前或者之后,而不能夹在中间。
另外,如果你想要使用多个位置参数,你需要在 add_argument
中指定 nargs
参数。例如:
parser.add_argument('input', nargs='+')
这行代码告诉 argparse
要接受一个或多个位置参数,并把它们放到一个列表里。想了解更多信息,可以查看 argparse 的文档。有了这行代码,下面的代码:
parser.parse_args(['-a', '-b', 'fileone', 'filetwo', 'filethree'])
会得到:
Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])
srgerg 对位置参数的定义是正确的。为了得到你想要的结果,你需要把它们当作可选参数来处理,并根据你的需求来调整结果的命名空间。
你可以使用自定义的操作:
class MyAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
# Set optional arguments to True or False
if option_string:
attr = True if values else False
setattr(namespace, self.dest, attr)
# Modify value of "input" in the namespace
if hasattr(namespace, 'input'):
current_values = getattr(namespace, 'input')
try:
current_values.extend(values)
except AttributeError:
current_values = values
finally:
setattr(namespace, 'input', current_values)
else:
setattr(namespace, 'input', values)
parser = argparse.ArgumentParser()
parser.add_argument('-a', nargs='+', action=MyAction)
parser.add_argument('-b', nargs='+', action=MyAction)
parser.add_argument('input', nargs='+', action=MyAction)
这样你就能得到:
>>> parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])
或者你可以像这样修改结果的命名空间:
>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-a', nargs='+')
>>> parser.add_argument('-b', nargs='+')
>>> parser.add_argument('input', nargs='+')
>>> result = parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
>>> inputs = []
>>> inputs.extend(result.a)
>>> inputs.extend(result.b)
>>> inputs.extend(result.input)
>>> modified = argparse.Namespace(
a = result.a != [],
b = result.b != [],
input = inputs)
这样你也能得到:
>>> modified
Namespace(a=True, b=True, input=['filetwo', 'filethree', 'fileone'])
不过,这两种方法都会导致代码变得不太容易阅读和维护。也许改变程序的逻辑,用其他方式来实现会更好。