Як я можу передати список як аргумент командного рядка з argparse?


442

Я намагаюся передати список як аргумент програмі командного рядка. Чи є 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параметра (залежно від того, як ви хочете, щоб користувальницький інтерфейс поводився).

нарги

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(або що завгодно), щоб отримати список вхідних даних (або будь-яких інших)

1 : Я взагалі не маю на увазі .. Я маю на увазі використання лапок для передачі списку -argparse це не те, що потрібно.


3
Як щодо списку рядків? Це перетворює кілька рядкових аргументів ("wassup", "щось" та "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 ...
0андрій

1
це може бути неінтуїтивно, якщо є позиційні аргументи, тому що argparse не може сказати, що має бути позиційним аргументом і що належить до nargs . --допомагає з'ясувати це, як показано в прикладі в моєму попередньому коментарі. Користувач IOW постачає --з усіма позиційними аргументами.
0андрій

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.
чепнер

13
@ chepner, так, ви абсолютно праві, і це було б більше пітонічно - просто невеликий помилок: int(time)повинен бути int(item). Мій приклад був спрощеною версією того, що я зазвичай роблю, де я перевіряю багато інших речей, а не просту обробку. Але, щоб просто відповісти на питання, я теж вважаю ваш шлях більш елегантним ..
dojuba

1
ця відповідь виглядає найбільш пітонічно
Кетцалькоатль

1
Коментар @chepner - це серйозна ніндзя
skillz

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 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 = '*', за замовчуванням = sample_list, help =" Рядок баз даних Приклади: \ -o option1 option2, -o option3 ")` `` Тут 'sample_list' є типовим списком із параметрами за замовчуванням. Напр .: sample_list = [option4, option5]
Py_minion

1
@Py_minion Дякую Сьогодні сьогодні його перевірятиму.
Мондра

Я це використав, це дуже корисно для передачі створення списків з аргументів.
сібі

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

Або ви можете написати спеціальний обробник / дію для розбору значень, розділених комами, щоб ви могли це зробити

-l 123,234,345 -l 456

5

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

Зауважте, що якщо потрібно пройти рядки, цей метод вимагає, щоб вони були цитують їх відповідним чином у командному рядку. Користувач може виявити це несподіваним. Якщо тільки розбирати цілі числа, це нормально.
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

Я думаю, що найелегантніше рішення - це передати лямбда-функцію «типу», про що згадував Чепнер. На додаток до цього, якщо ви заздалегідь не знаєте, яким буде роздільник списку, ви також можете передати кілька роздільників до 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
Ентоні

Змініть мій приклад, щоб він працював :)
Туманність
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.