ฉันจะส่งรายการเป็นอาร์กิวเมนต์บรรทัดคำสั่งด้วย argparse ได้อย่างไร


443

ฉันพยายามส่งรายการเป็นอาร์กิวเมนต์ไปยังโปรแกรมบรรทัดคำสั่ง มี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"

คำตอบ:


880

TL; DR

ใช้nargsตัวเลือกหรือการ'append'ตั้งค่าของactionตัวเลือก (ขึ้นอยู่กับว่าคุณต้องการให้อินเทอร์เฟซผู้ใช้ทำงานอย่างไร)

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=list argparseเคย


ลองมาดูรายละเอียดเพิ่มเติมเกี่ยวกับวิธีการต่าง ๆ ที่เราอาจลองทำและผลลัพธ์สุดท้าย

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

ประเด็นที่ :

  • ใช้nargsหรือaction='append'
    • nargsสามารถตรงไปตรงมามากขึ้นจากมุมมองของผู้ใช้ แต่มันไม่ได้ตั้งใจถ้ามีข้อโต้แย้งตำแหน่งเพราะargparseไม่สามารถบอกได้ว่าสิ่งที่ควรจะเป็นอาร์กิวเมนต์ตำแหน่งและสิ่งที่เป็นของnargs; หากคุณมีข้อโต้แย้งตำแหน่งแล้วaction='append'อาจท้ายเป็นตัวเลือกที่ดีกว่า
    • ข้างต้นเป็นเพียงความจริงถ้าnargsจะได้รับ'*', หรือ'+' '?'หากคุณระบุหมายเลขจำนวนเต็ม (เช่น4) จะไม่มีปัญหาในการเลือกตัวเลือกการผสมnargsและอาร์กิวเมนต์ตำแหน่งเนื่องจากargparseจะทราบจำนวนค่าที่คาดหวังสำหรับตัวเลือก
  • อย่าใช้เครื่องหมายคำพูดในบรรทัดคำสั่ง1
  • อย่าใช้type=listเพราะมันจะส่งคืนรายการ
    • สิ่งนี้เกิดขึ้นเพราะภายใต้ประทุนargparseใช้ค่าของtypeการบีบบังคับให้แต่ละอาร์กิวเมนต์ที่คุณเลือกtypeไม่ใช่การรวมของการขัดแย้งทั้งหมด
    • คุณสามารถใช้type=int(หรืออะไรก็ได้) เพื่อรับรายการ ints (หรืออะไรก็ตาม)

1 : ฉันไม่ได้หมายถึงโดยทั่วไป .. ฉันหมายถึงการใช้คำพูดเพื่อส่งรายการargparseไม่ใช่สิ่งที่คุณต้องการ


3
สิ่งที่เกี่ยวกับรายการของสตริงหรือไม่? สิ่งนี้จะเปลี่ยนอาร์กิวเมนต์สตริงหลายรายการ ("wassup", "some" และ "else") เป็นรายการของรายการที่มีลักษณะดังนี้: [['w', 'a', 's', 's', 'u' , 'p'], ['s', 'o', 'm', 'e', ​​'t', 'h', 'i', 'n', 'g'], ['e', ' l ',' s ',' e ']]
rd108

3
@ rd108 ฉันรู้ว่าฉันเดิมพันว่าคุณกำลังใช้type=listตัวเลือก อย่าใช้มัน ที่เปลี่ยนสตริงเป็นรายการและด้วยเหตุนี้รายการของรายการ
SethMMorton

1
@Dror อินพุตทั้งหมดจะถือว่าเป็นสตริงยกเว้นว่าคุณตั้งค่าtypeพารามิเตอร์เป็นวัตถุอื่น โดยค่าเริ่มต้นวิธีนี้จะส่งกลับรายการของสตริง
SethMMorton

1
--สามารถแยกตัวเลือกกับข้อโต้แย้งตำแหน่ง prog --opt1 par1 ... -- posp1 posp2 ...
0andriy

1
ก็สามารถ unintuitive ถ้ามีข้อโต้แย้งตำแหน่งเพราะ argparse ไม่สามารถบอกได้ว่าควรจะมีการโต้แย้งตำแหน่งและสิ่งที่เป็น nargs --ช่วยในการคิดออกตามที่แสดงในตัวอย่างในความคิดเห็นก่อนหน้าของฉัน IOW user supplies --ตามด้วยข้อโต้แย้งตำแหน่งทั้งหมด
0andriy

83

ฉันชอบผ่านสตริงที่มีตัวคั่นซึ่งฉันแยกวิเคราะห์ในสคริปต์ เหตุผลในการนี้คือ; รายการสามารถเป็นชนิดใดintหรือstrและบางครั้งใช้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]

จะทำงานได้ดี ตัวคั่นสามารถเป็นช่องว่างได้เช่นกันซึ่งจะบังคับใช้เครื่องหมายคำพูดรอบค่าอาร์กิวเมนต์เช่นในตัวอย่างในคำถาม


57
คุณสามารถตั้งค่าtypeอาร์กิวเมนต์เป็นlambda s: [int(time) for item in s.split(',')]แทนที่จะประมวลผลargs.listได้
chepner

13
@ chepner ใช่คุณขวาอย่างแน่นอนและมันจะเป็น pythonic มากขึ้น - เพียงแค่พิมพ์ผิดขนาดเล็ก: ควรจะเป็นint(time) int(item)ตัวอย่างของฉันเป็นเวอร์ชันที่เรียบง่ายของสิ่งที่ฉันทำโดยทั่วไปที่ฉันตรวจสอบสิ่งอื่น ๆ อีกมากมายแทนที่จะใช้การประมวลผลอย่างง่าย แต่เพื่อที่จะตอบคำถามฉันก็พบว่าวิธีการของคุณดูสง่างามยิ่งขึ้น ..
dojuba

1
คำตอบนี้ดูเหมือนจะไพเราะที่สุด
Quetzalcoatl

1
ความคิดเห็นโดย @chepner เป็นบางอย่าง ninja skillz +1
Briford Wylie

1
lambda items: list(csv.reader([items]))[0]ด้วยไลบรารีcsvมาตรฐานเป็นเวอร์ชันที่แก้ไขของความคิดเห็นจาก@chepnerสำหรับทุกคนที่กังวลเกี่ยวกับอินพุต CSV โดยพลการ (อ้างอิง: คำตอบจาก@adamk )
เควิน

19

นอกจากนี้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')

10

การใช้พารามิเตอร์ nargsในเมธอด add_argument ของ argparse

ฉันใช้ nargs = ' ' เป็นพารามิเตอร์ add_argument ฉันใช้ nargs = ' ' เป็นตัวเลือกเพื่อเลือกค่าเริ่มต้นหากฉันไม่ผ่านการขัดแย้งที่ชัดเจน

รวมถึงข้อมูลโค้ดเป็นตัวอย่าง:

ตัวอย่าง: temp_args1.py

โปรดทราบ:โค้ดตัวอย่างด้านล่างเขียนด้วย python3 โดยการเปลี่ยนรูปแบบคำสั่งพิมพ์สามารถเรียกใช้ใน 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 เป็น int

ผลการดำเนินการ:

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 มีวิธีใช้รายการเป็นอาร์กิวเมนต์และมีผลลัพธ์เป็นรายการด้วยหรือไม่ temp_args1.py -i [item5 ,item6, item7]และมีผลผลิตออกมาเป็นรายการเช่นกัน (แทนรายการที่ซ้อนกัน)
Moondra

@Moondra ใช่ ดีใจที่คุณถาม `` `parser.add_argument ('- o', '--options', action = 'store', dest = 'opt_list', type = str, nargs = '*', default = sample_list, help =" สตริงของฐานข้อมูล คั่นด้วยช่องว่างสีขาวตัวอย่าง: \ -o ตัวเลือก 1 ตัวเลือก 2, -o ตัวเลือก 3 ")` `` นี่ 'sample_list' เป็นรายการประเภทที่มีตัวเลือกเริ่มต้น ตัวอย่าง: sample_list = [ตัวเลือก 4, ตัวเลือก 5]
Py_minion

1
@Py_minion ขอบคุณ จะทดสอบในภายหลังวันนี้
Moondra

ฉันใช้สิ่งนี้มีประโยชน์มากสำหรับการผ่านการสร้างรายการจากอาร์กิวเมนต์
siby

5

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

ผลิต

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

หากคุณระบุอาร์กิวเมนต์เดียวกันหลายครั้งการกระทำเริ่มต้น ( 'store') จะแทนที่ข้อมูลที่มีอยู่เดิม

ทางเลือกคือการใช้การ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(' '))

ซึ่งผลิตผล

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

หรือคุณสามารถเขียน handler / action แบบกำหนดเองเพื่อแยกค่าที่คั่นด้วยเครื่องหมายจุลภาคเพื่อให้คุณสามารถทำได้

-l 123,234,345 -l 456

5

ในadd_argument(), typeเป็นเพียงวัตถุที่ได้รับ callable สตริงและผลตอบแทนที่คุ้มค่าตัวเลือก

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

โปรดทราบว่าหากจำเป็นต้องผ่านสตริงวิธีการนี้จะต้องการให้พวกเขาพูดอย่างเหมาะสมในบรรทัดคำสั่ง ผู้ใช้อาจพบสิ่งนี้โดยไม่คาดคิด ถ้าแยกเฉพาะจำนวนเต็มนี้เป็นเรื่องปกติ
SethMMorton

1

หากคุณมีรายการซ้อนอยู่ซึ่งรายการภายในมีประเภทและความยาวต่างกันและคุณต้องการรักษาประเภทไว้เช่น

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

0

ฉันต้องการจัดการผ่านหลายรายการค่าจำนวนเต็มและสตริง

ลิงก์ที่เป็นประโยชน์ => วิธีส่งตัวแปร 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ตัวแปรมีข้อโต้แย้งในการสั่งซื้อ


0

ฉันคิดว่าทางออกที่ดีที่สุดคือส่งฟังก์ชั่นแลมบ์ดาไปที่ "พิมพ์" ตามที่ 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']

คุณหมายถึง-lในการโทรตัวอย่าง? ในกรณีที่ไม่-nมาจากไหน?
แอนโทนี่

นอกจากนี้โซลูชันไม่ทำงานสำหรับฉันใน Python 3.8.2 นี่คือรหัส: parser.add_argument('-l', '--list', type = lambda s: re.split('[ ,;]', s)). นี่คืออินพุต: script.py -l abc xyz, abc\nxyz. ในที่สุดนี่คือผลลัพธ์:script.py: error: unrecognized arguments: xyz, abcnxyz
Anthony

เปลี่ยนตัวอย่างของฉันเพื่อที่มันจะได้ผล :)
Nebulastic
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.