在Python的argparse中多次使用相同的选项

问题描述 投票:0回答:5

我正在尝试编写一个脚本,该脚本接受多个输入源并对每个输入源执行一些操作。像这样的东西

./my_script.py \
    -i input1_url input1_name input1_other_var \
    -i input2_url input2_name input2_other_var \
    -i input3_url input3_name
# notice inputX_other_var is optional

但是我不太清楚如何使用

argparse
来做到这一点。似乎它被设置为每个选项标志只能使用一次。我知道如何将多个参数与单个选项(
nargs='*'
nargs='+'
)相关联,但这仍然不允许我多次使用
-i
标志。我该如何实现这个目标?

需要明确的是,我最终想要的是一个字符串列表的列表。所以

[["input1_url", "input1_name", "input1_other"],
 ["input2_url", "input2_name", "input2_other"],
 ["input3_url", "input3_name"]]
python argparse multiple-arguments
5个回答
133
投票

这是一个解析器,它处理重复的 2 个可选参数 - 名称在

metavar
:

中定义
parser=argparse.ArgumentParser()
parser.add_argument('-i','--input',action='append',nargs=2,
    metavar=('url','name'),help='help:')

In [295]: parser.print_help()
usage: ipython2.7 [-h] [-i url name]

optional arguments:
  -h, --help            show this help message and exit
  -i url name, --input url name
                        help:

In [296]: parser.parse_args('-i one two -i three four'.split())
Out[296]: Namespace(input=[['one', 'two'], ['three', 'four']])

这不能处理

2 or 3 argument
的情况(尽管我前段时间为 Python bug/问题编写了一个补丁来处理这样的范围)。

使用

nargs=3
metavar=('url','name','other')
单独定义参数怎么样?

元组

metavar
也可以与
nargs='+'
nargs='*'
一起使用; 2 个字符串用作
[-u A [B ...]]
[-u [A [B ...]]]


118
投票

这很简单;只需添加

action='append'
nargs='*'
(或
'+'
)。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-i', action='append', nargs='+')
args = parser.parse_args()

然后当你运行它时,你会得到

In [32]: run test.py -i input1_url input1_name input1_other_var -i input2_url i
...: nput2_name input2_other_var -i input3_url input3_name

In [33]: args.i
Out[33]:
[['input1_url', 'input1_name', 'input1_other_var'],
 ['input2_url', 'input2_name', 'input2_other_var'],
 ['input3_url', 'input3_name']]

34
投票

-i
应配置为接受 3 个参数并使用
append
操作。

>>> p = argparse.ArgumentParser()
>>> p.add_argument("-i", nargs=3, action='append')
_AppendAction(...)
>>> p.parse_args("-i a b c -i d e f -i g h i".split())
Namespace(i=[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']])

要处理可选值,您可以尝试使用简单的自定义类型。在本例中,

-i
的参数是一个以逗号分隔的字符串,分割数限制为 2。您需要对这些值进行后处理以确保至少指定两个值。

>>> p.add_argument("-i", type=lambda x: x.split(",", 2), action='append')
>>> print p.parse_args("-i a,b,c -i d,e -i g,h,i,j".split())
Namespace(i=[['a', 'b', 'c'], ['d', 'e'], ['g', 'h', 'i,j']])

要获得更多控制,请定义自定义操作。这个扩展了内置

_AppendAction
(由
action='append'
使用),但只是对提供给
-i
的参数数量进行一些范围检查。

class TwoOrThree(argparse._AppendAction):
    def __call__(self, parser, namespace, values, option_string=None):
        if not (2 <= len(values) <= 3):
            raise argparse.ArgumentError(self, "%s takes 2 or 3 values, %d given" % (option_string, len(values)))
        super(TwoOrThree, self).__call__(parser, namespace, values, option_string)

p.add_argument("-i", nargs='+', action=TwoOrThree)

19
投票

在此线程中添加其他内容。

如果您在

action='append'
中使用
add_argument()
,那么每次添加选项时,您都会在列表中的列表中获得参数。

如你所愿:

[
   ["input1_url", "input1_name", "input1_other"],
   ["input2_url", "input2_name", "input2_other"],
   ["input3_url", "input3_name"]
]

但是,如果有人希望这些参数位于同一个

list[]
中,请在代码中使用
action='extend'
而不是
action='append'
。这将在一个列表中为您提供这些参数。

[
  "input1_url", 
  "input1_name", 
  "input1_other", 
  "input2_url", 
  "input2_name", 
  "input2_other", 
  "input3_url", 
  "input3_name"
]

0
投票

我使用 nargs=1 和 action="append" 的组合作为可选参数,并得到了我想要的

#!/usr/bin/env python3
"""Argparse test"""
from typing import List
import argparse
import sys
import textwrap
if __name__ == "__main__":
    descr = textwrap.dedent(
        """\
        Demonstrate multiple optional attts
    """
    )
    usage = textwrap.dedent(
        """\
    test.py [-d] [-s val] file file
    """
    )
    parser = argparse.ArgumentParser(
        prog="test.py",
        description=descr,
        usage=usage,
        formatter_class=argparse.RawTextHelpFormatter,
    )
    parser.add_argument("-d", "--debug", action="store_true", help="set debug output")
    parser.add_argument("-s", "--skip", nargs = 1, action="append", help="skip")
    parser.add_argument("files", nargs=2, help="files")
    args = parser.parse_args()
    skip_list: List[str] = []
    # Note: args.skip is a list of lists
    if args.skip is not None:
        for inner_list in args.skip:
            for val in inner_list:
                skip_list.append(val)
    print(f"debug={args.debug}")
    print(f"skip-list={skip_list}")
    print(f"files={args.files}")
    sys.exit()

它按预期工作

>./test.py file_a file_b
debug=False
skip-list=[]
files=['file_a', 'file_b']
~/python_test
> ./test.py -d -s a -s b file_a file_b
debug=True
skip-list=['a', 'b']
files=['file_a', 'file_b']
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.