Làm thế nào tôi có thể vượt qua một danh sách như là một đối số dòng lệnh với argparse?


441

Tôi đang cố gắng chuyển một danh sách làm đối số cho chương trình dòng lệnh. Có một argparsetùy chọn để vượt qua một danh sách như tùy chọn?

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

Kịch bản được gọi như dưới đây

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

Câu trả lời:


879

TL; DR

Sử dụng nargstùy chọn hoặc 'append'cài đặt của actiontùy chọn (tùy thuộc vào cách bạn muốn giao diện người dùng hoạt động).

xà cừ

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

nargs='+'mất 1 hoặc nhiều đối số, nargs='*'mất 0 hoặc nhiều hơn.

chắp thêm

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

Với appendbạn cung cấp tùy chọn nhiều lần để xây dựng danh sách.

Đừng dùng type=list!!! - Có lẽ không có tình huống mà bạn muốn sử dụng type=listvới argparse. Không bao giờ.


Chúng ta hãy xem chi tiết hơn về một số cách khác nhau mà người ta có thể cố gắng để làm điều này, và kết quả cuối cùng.

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)

Đây là đầu ra bạn có thể mong đợi:

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

Hành trình :

  • Sử dụng nargshoặcaction='append'
    • nargscó thể đơn giản hơn từ góc độ người dùng, nhưng có thể không trực quan nếu có các đối số vị trí vì argparsekhông thể cho biết đâu là đối số vị trí và thuộc về cái gì nargs; nếu bạn có lập luận theo vị trí thì action='append'cuối cùng có thể là một lựa chọn tốt hơn.
    • Trên đây chỉ là đúng nếu nargsđược đưa ra '*', '+'hoặc '?'. Nếu bạn cung cấp một số nguyên (chẳng hạn như 4) thì sẽ không có vấn đề trộn các tùy chọn với nargsvà đối số vị trí bởi vì argparsesẽ biết chính xác có bao nhiêu giá trị mong đợi cho tùy chọn.
  • Không sử dụng dấu ngoặc kép trên dòng lệnh 1
  • Không sử dụng type=list, vì nó sẽ trả về một danh sách các danh sách
    • Điều này xảy ra bởi vì bên dưới mui xe argparsesử dụng giá trị typeđể ép buộc từng đối số đã cho mà bạn chọn type, không phải tổng hợp của tất cả các đối số.
    • Bạn có thể sử dụng type=int(hoặc bất cứ điều gì) để có được danh sách ints (hoặc bất cứ điều gì)

1 : Tôi không có ý nói chung .. Tôi có nghĩa là sử dụng dấu ngoặc kép để vượt qua một danh sáchargparse không phải là điều bạn muốn.


3
Thế còn một danh sách các chuỗi? Điều này biến nhiều đối số chuỗi ("wassup", "cái gì đó" và "khác") thành một danh sách các danh sách trông như thế này: [['w', 'a', 's', 's', 'u' , 'p'], ['s', 'o', 'm', 'e', ​​'t', 'h', 'i', 'n', 'g'], ['e', ' l ',' s ',' e ']]
lần thứ 108

3
@ rd108 Tôi thấy, tôi cá rằng bạn đang sử dụng type=listtùy chọn. Đừng sử dụng nó. Điều đó biến một chuỗi thành một danh sách, và do đó danh sách các danh sách.
SethMMorton

1
@Dror Tất cả đầu vào được coi là chuỗi trừ khi bạn đặt typetham số cho một số đối tượng khác. Theo mặc định phương thức này trả về một danh sách các chuỗi.
SethMMorton

1
--có thể phân chia các tùy chọn so với các đối số vị trí. prog --opt1 par1 ... -- posp1 posp2 ...
0andriy

1
nó có thể là không trực quan nếu có các đối số vị trí bởi vì argparse không thể cho biết đâu là đối số vị trí và những gì thuộc về các nargs . --giúp tìm ra điều này như thể hiện trong ví dụ trong bình luận trước đây của tôi. Nguồn cung cấp người dùng IOW --theo sau bởi tất cả các đối số vị trí.
0andriy

83

Tôi thích chuyển một chuỗi phân tách mà tôi phân tích cú pháp sau này trong tập lệnh. Những lý do cho điều này là; danh sách có thể là bất kỳ loại inthay str, và đôi khi sử dụng nargstôi chạy vào vấn đề nếu có nhiều đối số tùy chọn và lập luận vị trí.

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

Sau đó,

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

hoặc là,

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

sẽ hoạt động tốt Dấu phân cách cũng có thể là một khoảng trắng, mặc dù sẽ thực thi các trích dẫn xung quanh giá trị đối số như trong ví dụ trong câu hỏi.


57
Bạn có thể đặt typeđối số thành lambda s: [int(time) for item in s.split(',')]thay vì xử lý hậu kỳ args.list.
chepner

13
@ chepner, vâng, bạn hoàn toàn đúng và nó sẽ hay hơn pythonic - chỉ là một lỗi đánh máy nhỏ: int(time)nên như vậy int(item). Ví dụ của tôi là một phiên bản đơn giản hóa của những gì tôi thường làm, nơi tôi kiểm tra nhiều thứ khác thay vì xử lý đơn giản. Nhưng để trả lời câu hỏi, tôi cũng thấy cách của bạn thanh lịch hơn ..
dojuba

1
câu trả lời này có vẻ là pythonic nhất
Quetzalcoatl

1
Nhận xét của @chepner là một số kỹ năng ninja nghiêm túc +1
Briford Wylie

1
lambda items: list(csv.reader([items]))[0]với thư viện csv tiêu chuẩn là phiên bản sửa đổi của nhận xét từ @chepner cho bất kỳ ai lo lắng về đầu vào CSV tùy ý (ref: answer từ @adamk ).
Kevin

19

Ngoài ra nargs, bạn có thể muốn sử dụng choicesnếu bạn biết trước danh sách:

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

10

Sử dụng tham số nargs trong phương thức add_argument của argparse

Tôi sử dụng nargs = ' ' làm tham số add_argument. Tôi đặc biệt đã sử dụng nargs = ' ' cho tùy chọn để chọn mặc định nếu tôi không chuyển bất kỳ đối số rõ ràng nào

Bao gồm một đoạn mã làm ví dụ:

Ví dụ: temp_args1.py

Xin lưu ý: Mã mẫu dưới đây được viết bằng python3. Bằng cách thay đổi định dạng câu lệnh in, có thể chạy trong 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))

Lưu ý: Tôi đang thu thập nhiều đối số chuỗi được lưu trữ trong danh sách - opts.alist Nếu bạn muốn danh sách các số nguyên, hãy thay đổi tham số loại trên Parser.add_argument thành int

Kết quả thực hiện:

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

1
@Py_minion Có cách nào để sử dụng danh sách làm đối số và cũng có đầu ra là danh sách không? temp_args1.py -i [item5 ,item6, item7]và có đầu ra cũng là một danh sách (thay vì danh sách lồng nhau)
Moondra

@Moondra Vâng. rất vui khi bạn hỏi `` `Parser.add_argument ('- o', '--options', action = 'store', Dest = 'opt_list', type = str, nargs = '*', default = sample_list, help =" Chuỗi cơ sở dữ liệu Được ngăn cách bởi khoảng trắng. Ví dụ: \ -o tùy chọn1 tùy chọn2, -o tùy chọn3 ")` `Ở đây 'sample_list' là danh sách loại với các tùy chọn mặc định. Ví dụ: sample_list = [tùy chọn4, tùy chọn5]
Py_minion

1
@Py_minion Cảm ơn bạn. Đi để kiểm tra nó ra sau ngày hôm nay.
Moondra

Tôi đã sử dụng cái này, cái này rất hữu ích để chuyển danh sách tạo từ các đối số.
chị em

5

Nếu bạn đang có ý định thực hiện một chuyển đổi duy nhất có nhiều tham số, thì bạn sử dụng nargs='+'. Nếu ví dụ của bạn '-l' thực sự lấy số nguyên:

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

Sản xuất

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

Nếu bạn chỉ định cùng một đối số nhiều lần, hành động mặc định ( 'store') sẽ thay thế dữ liệu hiện có.

Thay thế là sử dụng appendhành động:

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

Sản xuất

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

Hoặc bạn có thể viết trình xử lý / hành động tùy chỉnh để phân tích các giá trị được phân tách bằng dấu phẩy để bạn có thể làm

-l 123,234,345 -l 456

5

Trong add_argument(), typechỉ là một đối tượng có thể gọi được nhận chuỗi và trả về giá trị tùy chọn.

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

Điều này sẽ cho phép:

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

Lưu ý rằng nếu cần phải truyền chuỗi, phương thức này sẽ yêu cầu họ trích dẫn chúng một cách thích hợp trên dòng lệnh. Một người dùng có thể tìm thấy điều này bất ngờ. Nếu chỉ phân tích số nguyên thì điều này là tốt.
SethMMorton

0

Nếu bạn có một danh sách lồng nhau trong đó các danh sách bên trong có các loại và độ dài khác nhau và bạn muốn giữ nguyên loại, ví dụ:

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

sau đó bạn có thể sử dụng giải pháp được đề xuất bởi @ sam-mason cho câu hỏi này , được hiển thị bên dưới:

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

cung cấp cho:

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

0

Tôi muốn xử lý chuyển qua nhiều danh sách, giá trị nguyên và chuỗi.

Liên kết hữu ích => Làm cách nào để chuyển một biến Bash cho 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:])

Thứ tự không quan trọng. Nếu bạn muốn vượt qua một danh sách chỉ cần làm như ở giữa "[""]và tách chúng bằng dấu phẩy.

Sau đó,

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

Kết quả => ['my_string', '3', ['1', '2'], ['3', '4', '5']], my_argsbiến chứa các đối số theo thứ tự.


0

Tôi nghĩ rằng giải pháp tao nhã nhất là chuyển một hàm lambda sang "gõ", như Chepner đã đề cập. Ngoài ra, nếu bạn không biết trước dấu phân cách trong danh sách của mình, bạn cũng có thể chuyển nhiều dấu phân cách để 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']

Ý của bạn là -ltrong ví dụ cuộc gọi? Nơi nào đã -nđến?
Anthony

Ngoài ra, giải pháp không hoạt động với tôi trong Python 3.8.2. Đây là mã : parser.add_argument('-l', '--list', type = lambda s: re.split('[ ,;]', s)). Đây là đầu vào : script.py -l abc xyz, abc\nxyz. Cuối cùng, đây là kết quả:script.py: error: unrecognized arguments: xyz, abcnxyz
Anthony

Thay đổi ví dụ của tôi để nó hoạt động :)
Nebulastic
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.