Bagaimana saya bisa meneruskan daftar sebagai argumen baris perintah dengan argparse?

443

Saya mencoba meneruskan daftar sebagai argumen ke program baris perintah. Apakah ada argparseopsi untuk lulus daftar sebagai opsi?

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

Script disebut seperti di bawah ini

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

Jawaban:

882

TL; DR

Gunakan nargsopsi atau 'append'pengaturan actionopsi (tergantung bagaimana Anda ingin antarmuka pengguna berperilaku).

nargs

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

nargs='+'membutuhkan 1 atau lebih argumen, nargs='*'membutuhkan nol atau lebih.

menambahkan

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

Dengan appendAnda memberikan opsi beberapa kali untuk membangun daftar.

Jangan gunakan type=list!!! - Mungkin tidak ada situasi di mana Anda akan ingin menggunakan type=listdengan argparse. Pernah.


Mari kita lihat lebih detail beberapa cara berbeda yang bisa dilakukan seseorang untuk melakukan ini, dan hasil akhirnya.

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)

Inilah output yang bisa Anda harapkan:

$ 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 :

  • Gunakan nargsatauaction='append'
    • nargsbisa lebih mudah dari perspektif pengguna, tetapi bisa tidak intuitif jika ada argumen posisi karena argparsetidak bisa mengatakan apa yang seharusnya menjadi argumen posisi dan apa yang menjadi milik nargs; jika Anda memiliki argumen posisi maka action='append'mungkin berakhir menjadi pilihan yang lebih baik.
    • Di atas hanya berlaku jika nargsdiberikan '*', '+'atau '?'. Jika Anda memberikan angka integer (seperti 4) maka tidak akan ada masalah dengan opsi campuran nargsdan argumen posisi karena argparseakan tahu persis berapa banyak nilai yang diharapkan untuk opsi tersebut.
  • Jangan gunakan kutipan di baris perintah 1
  • Jangan gunakan type=list, karena akan mengembalikan daftar daftar
    • Ini terjadi karena di bawah tenda argparsemenggunakan nilai typeuntuk memaksa setiap individu memberikan argumen yang Anda pilih type, bukan agregat dari semua argumen.
    • Anda dapat menggunakan type=int(atau apa pun) untuk mendapatkan daftar int (atau apa pun)

1 : Maksud saya tidak secara umum .. Maksud saya menggunakan tanda kutip untuk memberikan daftarargparse bukan yang Anda inginkan.

SethMMorton
sumber
3
Bagaimana dengan daftar string? Ini mengubah beberapa argumen string ("wassup", "sesuatu", dan "lain") menjadi daftar daftar yang terlihat seperti ini: [['w', 'a', 's', 's', 's', 'u' , 'p'], ['s', 'o', 'm', 'e', ​​'t', 'h', 'i', 'n', 'g'], ['e', ' l ',' s ',' e ']]
rd108
3
@ rd108 Begitu, saya yakin Anda menggunakan type=listopsi ini. Jangan gunakan itu. Itu mengubah string menjadi daftar, dan karenanya daftar daftar.
SethMMorton
1
@Dor Semua input diasumsikan sebagai string, kecuali jika Anda mengatur typeparameter ke beberapa objek lain. Secara default metode ini mengembalikan daftar string.
SethMMorton
1
--dapat memecah opsi vs argumen posisi. prog --opt1 par1 ... -- posp1 posp2 ...
0andriy
1
itu bisa tidak intuitif jika ada argumen posisional karena argparse tidak bisa mengatakan apa yang seharusnya menjadi argumen posisional dan apa yang menjadi milik nargs . --membantu untuk mencari tahu ini seperti yang ditunjukkan dalam contoh di komentar saya sebelumnya. Persediaan pengguna TKI --diikuti oleh semua argumen posisi.
0andriy
83

Saya lebih suka melewatkan string yang dibatasi yang saya uraikan nanti dalam skrip. Alasan untuk ini adalah; daftar dapat dari jenis apa pun intatau str, dan kadang-kadang menggunakan nargssaya mengalami masalah jika ada beberapa argumen opsional dan argumen posisi.

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

Kemudian,

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

atau,

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

akan bekerja dengan baik. Pembatas bisa berupa spasi juga, yang akan memberlakukan tanda kutip di sekitar nilai argumen seperti pada contoh dalam pertanyaan.

dojuba
sumber
57
Anda dapat mengatur typeargumen lambda s: [int(time) for item in s.split(',')]alih-alih pasca-pemrosesan args.list.
chepner
13
@ chepner, ya Anda memang benar dan itu akan lebih pythonic - hanya kesalahan ketik kecil: int(time)seharusnya int(item). Contoh saya adalah versi sederhana dari apa yang biasanya saya lakukan, di mana saya memeriksa banyak hal lain daripada pemrosesan sederhana. Tapi untuk sekadar menjawab pertanyaan, saya juga menemukan cara Anda lebih elegan ..
dojuba
1
jawaban ini tampaknya yang paling pythonic
Quetzalcoatl
1
Komentar dari @chepner adalah beberapa skill ninja serius +1
Briford Wylie
1
lambda items: list(csv.reader([items]))[0]dengan pustaka csv standar adalah versi modifikasi dari komentar dari @chepner untuk siapa pun yang khawatir tentang input CSV sewenang-wenang (ref: answer from @adamk ).
Kevin
19

Selain itu nargs, Anda mungkin ingin menggunakan choicesjika mengetahui daftar sebelumnya:

>>> 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')
Martin Thoma
sumber
10

Menggunakan parameter nargs dalam metode add_argument argparse

Saya menggunakan nargs = ' ' sebagai parameter add_argument. Saya secara khusus menggunakan nargs = ' ' ke opsi untuk memilih default jika saya tidak memberikan argumen eksplisit

Termasuk cuplikan kode sebagai contoh:

Contoh: temp_args1.py

Harap Dicatat: Kode contoh di bawah ini ditulis dalam python3. Dengan mengubah format pernyataan cetak, dapat berjalan di 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))

Catatan: Saya mengumpulkan beberapa argumen string yang disimpan dalam daftar - opts.alist Jika Anda ingin daftar bilangan bulat, ubah parameter tipe pada parser.add_argument menjadi int

Hasil Eksekusi:

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']
Py_minion
sumber
1
@Py_minion Apakah ada cara untuk menggunakan daftar sebagai argumen, dan memiliki output sebagai daftar juga? temp_args1.py -i [item5 ,item6, item7]dan minta outputnya keluar sebagai daftar juga (bukan daftar bersarang)
Moondra
@ Moondra Ya. senang kamu bertanya. `` `parser.add_argument ('- o', '--options', action = 'store', dest = 'opt_list', tipe = str, nargs = '*', default = sample_list, help =" String database dipisahkan oleh spasi putih. Contoh: \ -o option1 option2, -o option3 ")` `` Here 'sample_list' adalah daftar jenis dengan opsi default. Contoh: sample_list = [option4, option5]
Py_minion
1
@Py_minion Terima kasih. Akan mengujinya hari ini.
Moondra
Saya menggunakan ini, ini sangat berguna untuk membuat daftar dari argumen.
siby
5

Jika Anda bermaksud membuat satu sakelar, ambil beberapa parameter, lalu gunakan nargs='+'. Jika contoh Anda '-l' sebenarnya mengambil bilangan bulat:

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

Menghasilkan

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

Jika Anda menentukan argumen yang sama beberapa kali, tindakan default ( 'store') menggantikan data yang ada.

Alternatifnya adalah menggunakan appendtindakan:

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

Yang menghasilkan

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

Atau Anda bisa menulis custom handler / action untuk mengurai nilai yang dipisahkan koma sehingga Anda bisa melakukannya

-l 123,234,345 -l 456
kfsone
sumber
5

In add_argument(), typehanyalah objek yang bisa dipanggil yang menerima string dan mengembalikan nilai opsi.

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

Ini akan memungkinkan untuk:

$ ./tool --list "[1,2,3,4]"
wonder.mice
sumber
Perhatikan bahwa jika seseorang perlu melewati string, metode ini akan mengharuskan mereka mengutipnya dengan tepat di baris perintah. Seorang pengguna mungkin menemukan ini tidak terduga. Jika hanya parsing integer ini tidak masalah.
SethMMorton
1

Jika Anda memiliki daftar bersarang di mana daftar bagian dalam memiliki tipe dan panjang yang berbeda dan Anda ingin mempertahankan tipe tersebut, misalnya,

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

maka Anda dapat menggunakan solusi yang diusulkan oleh @ sam-mason untuk pertanyaan ini , yang ditunjukkan di bawah ini:

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

pemberian yang mana:

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

Saya ingin menangani melewati beberapa daftar, nilai integer, dan string.

Tautan Bermanfaat => Bagaimana cara mengirim variabel Bash ke 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:])

Ketertiban tidak penting. Jika Anda ingin meneruskan daftar, lakukan seperti di antara "["dan "]dan pisahkan menggunakan koma.

Kemudian,

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

Output => ['my_string', '3', ['1', '2'], ['3', '4', '5']], my_argsvariabel berisi argumen secara berurutan.

Alper
sumber
0

Saya pikir solusi paling elegan adalah dengan melewati fungsi lambda untuk "mengetik", seperti yang disebutkan oleh Chepner. Selain itu, jika Anda tidak tahu sebelumnya apa pembatas daftar Anda akan menjadi, Anda juga dapat melewati beberapa pembatas untuk 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']
Nebulastik
sumber
Apakah yang Anda maksud -ldalam contoh panggilan? Dari mana -ndatangnya?
Anthony
Juga, solusinya tidak bekerja untuk saya di Python 3.8.2. Ini kodenya:parser.add_argument('-l', '--list', type = lambda s: re.split('[ ,;]', s)) . Berikut adalah input: script.py -l abc xyz, abc\nxyz. Akhirnya, inilah hasilnya:script.py: error: unrecognized arguments: xyz, abcnxyz
Anthony
Ubah contoh saya agar berfungsi :)
Nebulastic