Python argparse - разные наборы опций

У меня есть программа с именем program.py, и я использую argparse для анализа аргументов.

У меня есть два режима, с которыми я хочу запустить этот двоичный файл: 1) Симуляция, которая не требует аргументов 2) Не симуляция, которая требует много аргументов

Я хочу, чтобы программа либо приняла

python program --simulation

или же

python program arg1 arg2 arg3 arg4

Где все "аргументы" необходимы.

Единственный способ сделать это - добавить "required = False" во все поля и вручную проверить логику, но мне было интересно, есть ли более элегантный способ.

Вот урезанная версия кода, который у меня есть

def get_args():
    parser = argparse.ArgumentParser(description = "program")
    parser.add_argument("arg1", type = bool)
    parser.add_argument("arg2" ,type = str)
    parser.add_argument("arg3", type = int)
    parser.add_argument("arg4", type = str)
    parser.add_argument("--simulation")
    args = parser.parse_args()
    return args

2 ответа

Решение

argparse не может быть таким умным. Однако в вашем простом случае вы можете "помочь" выбрать правильные параметры для разбора:

def get_args(args=sys.argv[1:]):
    parser = argparse.ArgumentParser(description = "program")
    if args and args[0].startswith("--"):
        parser.add_argument("--simulation")
    else:
        parser.add_argument("arg1", type = bool)
        parser.add_argument("arg2" ,type = str)
        parser.add_argument("arg3", type = int)
        parser.add_argument("arg4", type = str)
    args = parser.parse_args(args=args)
    return args

так print(get_args("--simulation xx".split())) выходы:

Namespace(simulation='xx')

потому что первый аргумент начинается с --, Любые другие параметры не удается выполнить синтаксический анализ командной строки, как ожидалось.

а также print(get_args("True foo 3 bar".split())) выходы:

Namespace(arg1=True, arg2='foo', arg3=3, arg4='bar')

забывание одного из 4-х позиционных параметров приводит к ошибке синтаксического анализа командной строки, как и ожидалось.

Кстати, я добавил параметр по умолчанию, который, если он опущен, читает из системных аргументов (как это было в вашем коде). Иначе вы можете читать из текстового файла и передавать токены args. Так что проще тестировать и создавать модули, которые можно вызывать с параметрами из других модулей, без необходимости взлома sys.argv,

Это явно неловкая спецификация для argparseи я подозреваю, что большинство других парсеров стиля POSIX.

сканирование sys.argv и корректировка определения синтаксического анализатора является одним из возможных способов.

Другой - использовать 2-х этапный парсер, с parse_known_args:

import argparse
usage = 'prog [-h] [--simulation] [arg1 arg2 arg3 arg4]'
parser1 = argparse.ArgumentParser(usage=usage)
parser1.add_argument('--simulation', action='store_true')
# or omit the `store_true` if it just takes one argument
# other possible optionals

parser2 = argparse.ArgumentParser()
#parser2.add_argument("arg1", type = bool)  # not a valid type parameter
parser2.add_argument("arg2" )
parser2.add_argument("arg3", type = int)
parser2.add_argument("arg4")
# positionals are required, unless nargs=? or *

args, extras = parser1.parse_known_args()
if not args.simulation:
    args = parser2.parse_args(extras, namespace=args)
elif extras:
    parser1.error('cannot use --simulation with args')
print(args)

Возможные пробеги включают в себя:

1526:~/mypy$ python stack41556997.py -h
usage: prog [-h] [--simulation] [arg1 arg2 arg3 arg4]

optional arguments:
  -h, --help    show this help message and exit
  --simulation

1526:~/mypy$ python stack41556997.py --simulation
Namespace(simulation=True)

1527:~/mypy$ python stack41556997.py 1 2 3
Namespace(arg2='1', arg3=2, arg4='3', simulation=False)

1527:~/mypy$ python stack41556997.py 1 2 3 --sim
usage: prog [-h] [--simulation] [arg1 arg2 arg3 arg4]
stack41556997.py: error: cannot use --simulation with args

Обратите внимание, что помощь не включает оба набора. Я включил некоторую информацию в пользовательское использование, но нет строк справки для arg#, Генерация хорошего help сообщение будет неудобным с вашей спецификацией.

Я пропустил твой arg1, type=bool не является действительным type аргумент. Смотрите мое объяснение в Разбор логических значений с argparse

Я изменился --simulation в store_true так как ты сказал, что не принимал никаких аргументов. Это нормальный способ принять Истину / Ложь.

Подпарсеры часто являются лучшим инструментом для принятия различных шаблонов аргументов. В этом случае у вас может быть один подпараметр с именем "имитация", который не требует никаких аргументов, и другой вызов "что-то другое", требующий 4 позиционирования.

Я собирался предложить взаимно-эксклюзивную группу с --simulation а также --other ДОПОЛНИТЕЛЬНО. Но store_true аргумент не работает в такой группе.

=============

Подпарсерский маршрут:

parser = argparse.ArgumentParser()
sp = parser.add_subparsers(dest='cmd')
sp.add_parser('simulate')
parser2 = sp.add_parser('other')
parser2.add_argument("arg2" )
parser2.add_argument("arg3", type = int)
parser2.add_argument("arg4")
print(parser.parse_args())

тестирование:

1552:~/mypy$ python stack41556997.py -h
usage: stack41556997.py [-h] {simulate,other} ...

positional arguments:
  {simulate,other}

optional arguments:
  -h, --help        show this help message and exit
1557:~/mypy$ python stack41556997.py simulate
Namespace(cmd='simulate')
1557:~/mypy$ python stack41556997.py other -h
usage: stack41556997.py other [-h] arg2 arg3 arg4

positional arguments:
  arg2
  arg3
  arg4

optional arguments:
  -h, --help  show this help message and exit
1557:~/mypy$ python stack41556997.py other 1 2 3
Namespace(arg2='1', arg3=2, arg4='3', cmd='other')

Обратите внимание, что arg3type преобразовал ввод в целое число. Остальные оставлены в виде строк. С этой настройкой args.cmd будет имя подпаратера, не совсем то же самое, что и логическое значение args.simulation приписывать.

==================

Отмеченный аргумент не требуется по умолчанию. Позиционные аргументы требуются, если только nargs значение равно? или же '*'. Вы не можете предоставить "обязательный" параметр позиционеру.

Немного более простой подход, предполагающий, что вы готовы игнорировать любые дополнительные аргументы, если --simulation аргумент, это что-то вроде:

parser = argparse.ArgumentParser(description = "program")
parser.add_argument("arg1", type = bool)
parser.add_argument("arg2" ,type = str)
parser.add_argument("arg3", type = int)
parser.add_argument("arg4", type = str)
parser.add_argument("--simulation")

if "--simulation" in sys.argv:
    simulation()
else:
    args = parser.parse_args()
    main(args)
Другие вопросы по тегам