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')
Обратите внимание, что arg3
type
преобразовал ввод в целое число. Остальные оставлены в виде строк. С этой настройкой 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)