簡體   English   中英

如何使用 argparse 將列表作為命令行參數傳遞?

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

我正在嘗試將列表作為參數傳遞給命令行程序。 是否有argparse選項可以將列表作為選項傳遞?

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

腳本調用如下

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

TL; 博士

使用nargs選項或action選項的'append'設置(取決於您希望用戶界面的行為方式)。

nargs

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

nargs='+'需要 1 個或多個參數, nargs='*'需要零個或多個。

附加

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

使用append您可以多次提供選項來構建列表。

不要使用type=list !!! - 可能沒有您希望將type=listargparse一起使用的情況。 曾經。


讓我們更詳細地了解一些可能嘗試執行此操作的不同方式以及最終結果。

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)

這是您可以預期的輸出:

$ 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']

外賣

  • 使用nargsaction='append'
    • 從用戶的角度來看, nargs可能更直接,但如果有位置參數,則可能不直觀,因為argparse無法分辨什么應該是位置參數,什么屬於nargs 如果您有位置參數,那么action='append'最終可能是更好的選擇。
    • 僅當nargs被賦予'*''+''?' ,上述情況才成立'?' . 如果您提供一個整數(例如4 ),那么將選項與nargs和位置參數混合將沒有問題,因為argparse將確切知道該選項需要多少個值。
  • 不要在命令行上使用引號1
  • 不要使用type=list ,因為它會返回一個列表列表
    • 發生這種情況是因為在argparse使用type的值將每個給定的參數強制為您選擇的type ,而不是所有參數的聚合。
    • 您可以使用type=int (或其他)來獲取整數列表(或其他)

1 :我的意思不是一般的..我的意思是使用引號將列表傳遞給argparse不是你想要的。

我更喜歡傳遞我稍后在腳本中解析的分隔字符串。 這樣做的原因是; 列表可以是任何類型的intstr ,如果有多個可選參數和位置參數,有時使用nargs我會遇到問題。

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(',')]

然后,

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

要么,

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

會正常工作。 分隔符也可以是空格,但它會像問題中的示例一樣在參數值周圍強制使用引號。

或者您可以使用 Chepner 評論中建議的 lambda 類型:

parser.add_argument('-l', '--list', help='delimited list input', 
    type=lambda s: [int(item) for item in s.split(',')])

除了nargs ,如果您事先知道列表,您可能想要使用choices

>>> 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')

在 argparse 的 add_argument 方法中使用nargs 參數

我使用nargs='*'作為 add_argument 參數。 如果我沒有傳遞任何顯式參數,我專門使用nargs='*'來選擇默認值

包括一個代碼片段作為示例:

示例:temp_args1.py

請注意:以下示例代碼是用 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))

注意:我正在收集存儲在列表中的多個字符串參數 - opts.alist如果您想要整數列表, parser.add_argument上的類型參數parser.add_argumentint

執行結果:

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']

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")

這將允許:

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

如果您打算讓單個開關采用多個參數,那么您可以使用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='lst',      # store in 'lst'.
    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(' '))

生產

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

如果多次指定相同的參數,默認操作 ( 'store' ) 將替換現有數據。

另一種方法是使用append操作:

a = argparse.ArgumentParser()
a.add_argument(
    '-l', '--list',  # either of this switches
    type=int,        # /parameters/ are ints
    dest='lst',      # store in 'lst'.
    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(' '))

其中產生

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

或者您可以編寫自定義處理程序/操作來解析逗號分隔的值,以便您可以執行

-l 123,234,345 -l 456

我認為最優雅的解決方案是將 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']

也許是最簡單的答案

import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-l", "--tolist", help="input to list", action="store_true")
parser.add_argument("newlist", type=str, help="generate a list")
args = parser.parse_args()
if args.tolist:
    print(args.newlist.split(" "))

如果您有一個嵌套列表,其中內部列表具有不同的類型和長度,並且您想保留該類型,例如,

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

那么你可以使用@sam-mason提出的解決方案來解決這個問題,如下所示:

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]]'])

這使:

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

我想處理傳遞多個列表、整數值和字符串。

有用的鏈接 =>如何將 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:])

順序並不重要。 如果你想傳遞一個列表,只需在"[""]之間做,並用逗號分隔它們。

然后,

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

輸出 => ['my_string', '3', ['1', '2'], ['3', '4', '5']]my_args變量按順序包含參數。

您可以將列表解析為字符串並使用eval內置函數將其作為列表讀取。 在這種情況下,您必須將單引號放入雙引號(或其他方法)以確保成功解析字符串。

# declare the list arg as a string
parser.add_argument('-l', '--list', type=str)

# parse
args = parser.parse()

# turn the 'list' string argument into a list object
args.list = eval(args.list)
print(list)
print(type(list))

測試:

python list_arg.py --list "[1, 2, 3]"

[1, 2, 3]
<class 'list'>

JSON 列表解決方案

通過命令行處理傳遞列表(也就是字典)的一個好方法是使用json 這簡化了 argparse 解析,代價是需要一個單獨的步驟來解析 json。

# parse_list.py
import argparse
import json

parser = argparse.ArgumentParser()
parser.add_argument('-l', '--list', type=str)
args = parser.parse_args()
# parse string json input to python list
parsed_list = json.loads(args.list)
print(parsed_list)

示例用法

$ python parse_list.py -l "[265340, 268738, 270774, 270817]"
[265340, 268738, 270774, 270817]

請注意,如果您將action='append'default參數一起傳遞,Argparse 將嘗試 append 提供默認值,而不是替換您可能期望或可能不期望的默認值。

這是Argparse Docs 中給出的一個action='append示例。 在這種情況下,事情將按預期工作:

>> import argparse
>> parser = argparse.ArgumentParser()
>> parser.add_argument('--foo', action='append')
>> parser.parse_args('--foo 1 --foo 2'.split())

Out[2]: Namespace(foo=['1', '2'])

但是,如果您選擇提供默認值,Argparse 的“附加”操作將嘗試將 append 設置為提供的默認值,而不是替換默認值:

import argparse
REASONABLE_DEFAULTS = ['3', '4']
parser = argparse.ArgumentParser()
parser.add_argument('--foo', default=REASONABLE_DEFAULTS,action='append')
parser.parse_args('--foo 1 --foo 2'.split())

Out[6]: Namespace(foo=['3', '4', '1', '2'])

如果您希望Argparse替換默認值——例如傳入一個元組作為默認值,而不是一個列表——這可能會導致一些令人困惑的錯誤:

import argparse
REASONABLE_DEFAULTS = ('3', '4')
parser = argparse.ArgumentParser()
parser.add_argument('--foo', default=REASONABLE_DEFAULTS,action='append')
parser.parse_args('--foo 1 --foo 2'.split())

AttributeError: 'tuple' object has no attribute 'append'

有一個跟蹤這種意外行為的錯誤,但由於它可以追溯到 2012 年,因此不太可能得到解決。

將 chepner 的評論應用於Lunguini 的回答:

import argparse, json                                                                                            
parser = argparse.ArgumentParser()                                                                               
parser.add_argument('-l', '--list', type=lambda a: json.loads(a), default="[]",                                  
                    help="String formatted as list wrapped in []")                                               
args = parser.parse_args()                                                                                       
print(args.list)                                                                                                 

用法:

$ python parse_list.py -l "[265340, 268738, 270774, 270817]"
[265340, 268738, 270774, 270817]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM