Поддерживает ли argparse (python) взаимоисключающие группы аргументов? - PullRequest
21 голосов
/ 23 января 2011

Если у меня есть аргументы '-a', '-b', '-c', '-d', с функцией add_mutually_exclusive_group() моей программе придется использовать только один из них.Есть ли способ объединить это так, чтобы программа принимала только '-a 999 -b 999' или '-c 999 -d 999'?

Редактировать: добавив простую программу для большей ясности:

>>> parser = argparse.ArgumentParser()
>>> group = parser.add_mutually_exclusive_group()
>>> group.add_argument('-a')
>>> group.add_argument('-b')
>>> group.add_argument('-c')
>>> group.add_argument('-d')

Тогда может быть вызван только ./app.py -a | ./app.py -b | ./app.py -c | ./app.py -d.Можно ли иметь argparse group группы исключения, чтобы называться только ./app.py -a .. -b .. | ./app.py -c .. -d ..?

Ответы [ 3 ]

6 голосов
/ 23 января 2011

РЕДАКТИРОВАТЬ : Неважно. Потому что argparse делает ужасный выбор необходимости создания опции при вызове group.add_argument. Это не было бы моим выбором дизайна. Если вы отчаянно нуждаетесь в этой функции, вы можете попробовать сделать это с ConflictsOptionParser :

# exclusivegroups.py
import conflictsparse

parser = conflictsparse.ConflictsOptionParser()
a_opt = parser.add_option('-a')
b_opt = parser.add_option('-b')
c_opt = parser.add_option('-c')
d_opt = parser.add_option('-d')

import itertools
compatible_opts1 = (a_opt, b_opt)
compatible_opts2 = (c_opt, d_opt)
exclusives = itertools.product(compatible_opts1, compatible_opts2)
for exclusive_grp in exclusives:
    parser.register_conflict(exclusive_grp)


opts, args = parser.parse_args()
print "opts: ", opts
print "args: ", args

Таким образом, когда мы вызываем его, мы видим, что получаем желаемый эффект.

$ python exclusivegroups.py -a 1 -b 2
opts:  {'a': '1', 'c': None, 'b': '2', 'd': None}
args:  []
$ python exclusivegroups.py -c 3 -d 2
opts:  {'a': None, 'c': '3', 'b': None, 'd': '2'}
args:  []
$ python exclusivegroups.py -a 1 -b 2 -c 3
Usage: exclusivegroups.py [options]

exclusivegroups.py: error: -b, -c are incompatible options.

Предупреждающее сообщение не сообщает вам, что '-a' и '-b' несовместимы с '-c', однако может быть создано более подходящее сообщение об ошибке. Старый, неправильный ответ ниже.

ПОСЛЕДНЕЕ РЕДАКТИРОВАНИЕ: [Это редактирование неверно, хотя не будет ли это просто идеальный мир, если argparse сработает таким образом?] Мой предыдущий ответ на самом деле был неверным, Вы должны быть в состоянии сделать это с argparse, указав одну группу для взаимоисключающих опций. Мы можем даже использовать itertools для обобщения процесса. И сделайте так, чтобы нам не приходилось вводить все комбинации явно:

import itertools
compatible_opts1 = ('-a', '-b')
compatible_opts2 = ('-c', '-d')
exclusives = itertools.product(compatible_opts1, compatible_opts2)
for exclusive_grp in exclusives:
    group = parser.add_mutually_exclusive_group()
    group.add_argument(exclusive_grp[0])
    group.add_argument(exclusive_grp[1])
5 голосов
/ 20 ноября 2012

Просто наткнулся на эту проблему сам.Из моего прочтения argparse docs, кажется, нет простого способа достичь этого в argparse.Я подумал об использовании parse_known_args, но вскоре это приведет к написанию специализированной версии argparse ;-)

Возможно, отчет об ошибке в порядке.В то же время, если вы хотите, чтобы ваш пользователь сделал небольшую дополнительную печать, вы можете подделать его подгруппами (например, как работают аргументы git и svn), например,

    subparsers = parser.add_subparsers()
    p_ab = subparsers.add_parser('ab')
    p_ab.add_argument(...)

    p_cd = subparsers.add_parser('cd')
    p_cd.add_argument(...)

Не идеально, нопо крайней мере, это дает вам пользу от argparse без излишней хакерской атаки.Я закончил тем, что покончил с переключателями и просто использовал операции с подпарсерами с необходимыми подаргументами.

0 голосов
/ 20 июля 2018

Subparsers

Аналогично unhammer , но с большим пользовательским контролем. Примечание: я на самом деле не тестировал этот метод, но он должен работать теоретически и с возможностями Python.

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

# Python 3
import argparse

try:
    parser = argparse.ArgumentParser()
    parser.add_argument('-a')
    parser.add_argument('-b')
    args = parser.parse_args
except argparse.ArgumentError:
    parser = argparse.ArgumentParser()
    parser.add_argument('-c')
    parser.add_argument('-d')
    args = parser.parse_args
...