Python 如何使用 argparse 将列表作为命令行参数传递?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15753701/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:54:42  来源:igfitidea点击:

How can I pass a list as a command-line argument with argparse?

pythonargparse

提问by user2125827

I am trying to pass a list as an argument to a command line program. Is there an argparseoption to pass a list as option?

我试图将列表作为参数传递给命令行程序。是否可以argparse选择将列表作为选项传递?

parser.add_argument('-l', '--list',
                      type=list, action='store',
                      dest='list',
                      help='<Required> Set flag',
                      required=True)

Script is called like below

脚本调用如下

python test.py -l "265340 268738 270774 270817"

采纳答案by SethMMorton

TL;DR

TL; 博士

Use the nargsoption or the 'append'setting of the actionoption (depending on how you want the user interface to behave).

使用nargs选项或选项的'append'设置action(取决于您希望用户界面的行为方式)。

nargs

nargs

parser.add_argument('-l','--list', nargs='+', help='<Required> Set flag', required=True)
# Use like:
# python arg.py -l 1234 2345 3456 4567

nargs='+'takes 1 or more arguments, nargs='*'takes zero or more.

nargs='+'采用 1 个或多个参数,nargs='*'采用零个或多个参数。

append

附加

parser.add_argument('-l','--list', action='append', help='<Required> Set flag', required=True)
# Use like:
# python arg.py -l 1234 -l 2345 -l 3456 -l 4567

With appendyou provide the option multiple times to build up the list.

有了append您提供的选项多次以建立名单。

Don't use type=list!!!- There is probably no situation where you would want to use type=listwith argparse. Ever.

不要用type=list!!!- 可能没有您想要使用type=listwith 的情况argparse。曾经。



Let's take a look in more detail at some of the different ways one might try to do this, and the end result.

让我们更详细地了解一些可能尝试执行此操作的不同方式以及最终结果。

import argparse

parser = argparse.ArgumentParser()

# By default it will fail with multiple arguments.
parser.add_argument('--default')

# Telling the type to be a list will also fail for multiple arguments,
# but give incorrect results for a single argument.
parser.add_argument('--list-type', type=list)

# This will allow you to provide multiple arguments, but you will get
# a list of lists which is not desired.
parser.add_argument('--list-type-nargs', type=list, nargs='+')

# This is the correct way to handle accepting multiple arguments.
# '+' == 1 or more.
# '*' == 0 or more.
# '?' == 0 or 1.
# An int is an explicit number of arguments to accept.
parser.add_argument('--nargs', nargs='+')

# To make the input integers
parser.add_argument('--nargs-int-type', nargs='+', type=int)

# An alternate way to accept multiple inputs, but you must
# provide the flag once per input. Of course, you can use
# type=int here if you want.
parser.add_argument('--append-action', action='append')

# To show the results of the given option to screen.
for _, value in parser.parse_args()._get_kwargs():
    if value is not None:
        print(value)

Here is the output you can expect:

这是您可以预期的输出:

$ python arg.py --default 1234 2345 3456 4567
...
arg.py: error: unrecognized arguments: 2345 3456 4567

$ python arg.py --list-type 1234 2345 3456 4567
...
arg.py: error: unrecognized arguments: 2345 3456 4567

$ # Quotes won't help here... 
$ python arg.py --list-type "1234 2345 3456 4567"
['1', '2', '3', '4', ' ', '2', '3', '4', '5', ' ', '3', '4', '5', '6', ' ', '4', '5', '6', '7']

$ python arg.py --list-type-nargs 1234 2345 3456 4567
[['1', '2', '3', '4'], ['2', '3', '4', '5'], ['3', '4', '5', '6'], ['4', '5', '6', '7']]

$ python arg.py --nargs 1234 2345 3456 4567
['1234', '2345', '3456', '4567']

$ python arg.py --nargs-int-type 1234 2345 3456 4567
[1234, 2345, 3456, 4567]

$ # Negative numbers are handled perfectly fine out of the box.
$ python arg.py --nargs-int-type -1234 2345 -3456 4567
[-1234, 2345, -3456, 4567]

$ python arg.py --append-action 1234 --append-action 2345 --append-action 3456 --append-action 4567
['1234', '2345', '3456', '4567']

Takeaways:

外卖

  • Use nargsor action='append'
    • nargscan be more straightforward from a user perspective, but it can be unintuitive if there are positional arguments because argparsecan't tell what should be a positional argument and what belongs to the nargs; if you have positional arguments then action='append'may end up being a better choice.
    • The above is only true if nargsis given '*', '+', or '?'. If you provide an integer number (such as 4) then there will be no problem mixing options with nargsand positional arguments because argparsewill know exactly how many values to expect for the option.
  • Don't use quotes on the command line1
  • Don't use type=list, as it will return a list of lists
    • This happens because under the hood argparseuses the value of typeto coerce each individual given argumentyou your chosen type, not the aggregate of all arguments.
    • You can use type=int(or whatever) to get a list of ints (or whatever)
  • 使用nargsaction='append'
    • nargs从用户的角度来看可能更直接,但如果有位置参数argparse可能不直观,因为无法分辨什么应该是位置参数,什么属于nargs; 如果您有位置参数,那么action='append'最终可能会成为更好的选择。
    • 以上仅在nargs给出'*''+'、 或时才成立'?'。如果您提供一个整数(例如4),那么将选项与nargs和 位置参数混合将没有问题,因为argparse将确切知道该选项需要多少个值。
  • 不要在命令行上使用引号1
  • 不要使用type=list,因为它会返回一个列表列表
    • 发生这种情况是因为在幕后argparse使用 的值type来强制您选择的每个给定参数type,而不是所有参数的集合。
    • 您可以使用type=int(或其他)获取整数列表(或其他)


1: I don't mean in general.. I mean using quotes to pass a list to argparseis not what you want.

1:我的意思不是一般的..我的意思是使用引号将列表传递给argparse不是你想要的。

回答by Martin Thoma

Additionally to nargs, you might want to use choicesif you know the list in advance:

除了nargschoices如果您事先知道列表,您可能想使用:

>>> parser = argparse.ArgumentParser(prog='game.py')
>>> parser.add_argument('move', choices=['rock', 'paper', 'scissors'])
>>> parser.parse_args(['rock'])
Namespace(move='rock')
>>> parser.parse_args(['fire'])
usage: game.py [-h] {rock,paper,scissors}
game.py: error: argument move: invalid choice: 'fire' (choose from 'rock',
'paper', 'scissors')

回答by dojuba

I prefer passing a delimited string which I parse later in the script. The reasons for this are; the list can be of any type intor str, and sometimes using nargsI run into problems if there are multiple optional arguments and positional arguments.

我更喜欢传递我稍后在脚本中解析的分隔字符串。这样做的原因是;列表可以是任何类型intstrnargs如果有多个可选参数和位置参数,有时使用我会遇到问题。

parser = ArgumentParser()
parser.add_argument('-l', '--list', help='delimited list input', type=str)
args = parser.parse_args()
my_list = [int(item) for item in args.list.split(',')]

Then,

然后,

python test.py -l "265340,268738,270774,270817" [other arguments]

or,

或者,

python test.py -l 265340,268738,270774,270817 [other arguments]

will work fine. The delimiter can be a space, too, which would though enforce quotes around the argument value like in the example in the question.

会正常工作。分隔符也可以是空格,但它会像问题中的示例一样在参数值周围强制使用引号。

回答by Py_minion

Using nargs parameterin argparse's add_argument method

在 argparse 的 add_argument 方法中使用nargs 参数

I use nargs='' as an add_argument parameter. I specifically used nargs='' to the option to pick defaults if I am not passing any explicit arguments

我使用 nargs=' ' 作为 add_argument 参数。如果我没有传递任何显式参数,我专门使用 nargs='' 来选择默认值

Including a code snippet as example:

包括一个代码片段作为示例:

Example: temp_args1.py

示例:temp_args1.py

Please Note:The below sample code is written in python3. By changing the print statement format, can run in python2

请注意:以下示例代码是用 python3 编写的。通过改变print语句格式,可以在python2中运行

#!/usr/local/bin/python3.6

from argparse import ArgumentParser

description = 'testing for passing multiple arguments and to get list of args'
parser = ArgumentParser(description=description)
parser.add_argument('-i', '--item', action='store', dest='alist',
                    type=str, nargs='*', default=['item1', 'item2', 'item3'],
                    help="Examples: -i item1 item2, -i item3")
opts = parser.parse_args()

print("List of items: {}".format(opts.alist))

Note: I am collecting multiple string arguments that gets stored in the list - opts.alist If you want list of integers, change the type parameter on parser.add_argument to int

注意:我正在收集存储在列表中的多个字符串参数 - opts.alist 如果您想要整数列表,请将 parser.add_argument 上的类型参数更改为 int

Execution Result:

执行结果:

python3.6 temp_agrs1.py -i item5 item6 item7
List of items: ['item5', 'item6', 'item7']

python3.6 temp_agrs1.py -i item10
List of items: ['item10']

python3.6 temp_agrs1.py
List of items: ['item1', 'item2', 'item3']

回答by kfsone

If you are intending to make a single switch take multiple parameters, then you use nargs='+'. If your example '-l' is actually taking integers:

如果您打算让单个开关采用多个参数,那么您可以使用nargs='+'. 如果您的示例“-l”实际上采用整数:

a = argparse.ArgumentParser()
a.add_argument(
    '-l', '--list',  # either of this switches
    nargs='+',       # one or more parameters to this switch
    type=int,        # /parameters/ are ints
    dest='list',     # store in 'list'.
    default=[],      # since we're not specifying required.
)

print a.parse_args("-l 123 234 345 456".split(' '))
print a.parse_args("-l 123 -l=234 -l345 --list 456".split(' '))

Produces

生产

Namespace(list=[123, 234, 345, 456])
Namespace(list=[456])  # Attention!

If you specify the same argument multiple times, the default action ('store') replaces the existing data.

如果多次指定相同的参数,默认操作 ( 'store') 将替换现有数据。

The alternative is to use the appendaction:

另一种方法是使用以下append操作:

a = argparse.ArgumentParser()
a.add_argument(
    '-l', '--list',  # either of this switches
    type=int,        # /parameters/ are ints
    dest='list',     # store in 'list'.
    default=[],      # since we're not specifying required.
    action='append', # add to the list instead of replacing it
)

print a.parse_args("-l 123 -l=234 -l345 --list 456".split(' '))

Which produces

其中产生

Namespace(list=[123, 234, 345, 456])

Or you can write a custom handler/action to parse comma-separated values so that you could do

或者您可以编写自定义处理程序/操作来解析逗号分隔的值,以便您可以执行

-l 123,234,345 -l 456

回答by wonder.mice

In add_argument(), typeis just a callable object that receives string and returns option value.

add_argument(),type只是一个可调用对象,它接收字符串并返回选项值。

import ast

def arg_as_list(s):                                                            
    v = ast.literal_eval(s)                                                    
    if type(v) is not list:                                                    
        raise argparse.ArgumentTypeError("Argument \"%s\" is not a list" % (s))
    return v                                                                   


def foo():
    parser.add_argument("--list", type=arg_as_list, default=[],
                        help="List of values")

This will allow to:

这将允许:

$ ./tool --list "[1,2,3,4]"

回答by Meysam Sadeghi

If you have a nested list where the inner lists have different types and lengths and you would like to preserve the type, e.g.,

如果您有一个嵌套列表,其中内部列表具有不同的类型和长度,并且您希望保留该类型,例如,

[[1, 2], ["foo", "bar"], [3.14, "baz", 20]]

[[1, 2], ["foo", "bar"], [3.14, "baz", 20]]

then you can use the solution proposed by @sam-masonto this question, shown below:

那么你可以使用提出的解决方案@ SAM-石匠这个问题,如下图所示:

from argparse import ArgumentParser
import json

parser = ArgumentParser()
parser.add_argument('-l', type=json.loads)
parser.parse_args(['-l', '[[1,2],["foo","bar"],[3.14,"baz",20]]'])

which gives:

这使:

Namespace(l=[[1, 2], ['foo', 'bar'], [3.14, 'baz', 20]])

回答by alper

I want to handle passing multiple lists, integer values and strings.

我想处理传递多个列表、整数值和字符串。

Helpful link => How to pass a Bash variable to Python?

有用的链接 =>如何将 Bash 变量传递给 Python?

def main(args):
    my_args = []
    for arg in args:
        if arg.startswith("[") and arg.endswith("]"):
            arg = arg.replace("[", "").replace("]", "")
            my_args.append(arg.split(","))
        else:
            my_args.append(arg)

    print(my_args)


if __name__ == "__main__":
    import sys
    main(sys.argv[1:])


Order is not important. If you want to pass a list just do as in between "["and "]and seperate them using a comma.

顺序并不重要。如果你想传递一个列表,只需在"["and之间做,"]并使用逗号分隔它们。

Then,

然后,

python test.py my_string 3 "[1,2]" "[3,4,5]"

Output => ['my_string', '3', ['1', '2'], ['3', '4', '5']], my_argsvariable contains the arguments in order.

输出 => ['my_string', '3', ['1', '2'], ['3', '4', '5']]my_args变量按顺序包含参数。

回答by Nebulastic

I think the most elegant solution is to pass a lambda function to "type", as mentioned by Chepner. In addition to this, if you do not know beforehand what the delimiter of your list will be, you can also pass multiple delimiters to re.split:

我认为最优雅的解决方案是将 lambda 函数传递给“类型”,正如 Chepner 所提到的。除此之外,如果您事先不知道列表的分隔符是什么,您还可以将多个分隔符传递给 re.split:

# python3 test.py -l "abc xyz, 123"

import re
import argparse

parser = argparse.ArgumentParser(description='Process a list.')
parser.add_argument('-l', '--list',
                    type=lambda s: re.split(' |, ', s),
                    required=True,
                    help='comma or space delimited list of characters')

args = parser.parse_args()
print(args.list)


# Output: ['abc', 'xyz', '123']