Python Argparse - необязательный аргумент добавления с выбором - PullRequest
17 голосов
/ 16 декабря 2011

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

, когда пользователь не дает аргументов, он получает ошибку, что выбор по умолчанию неверен

acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', action='append', choices=acts, default=[['dump', 'clear']])
args = p.parse_args([])
>>> usage: [-h] [{clear,copy,dump,lock} [{clear,copy,dump,lock} ...]]
: error: argument action: invalid choice: [['dump', 'clear']] (choose from 'clear', 'copy', 'dump', 'lock')

и когдаОпределите набор действий, к результирующему пространству имен добавляются действия пользователя по умолчанию, а не замена по умолчанию

acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', action='append', choices=acts, default=[['dump', 'clear']])
args = p.parse_args(['lock'])
args
>>> Namespace(action=[['dump', 'clear'], ['dump']])

Ответы [ 5 ]

15 голосов
/ 16 декабря 2011

То, что вам нужно, можно сделать с помощью настроенного argparse.Action, как показано в следующем примере:

import argparse

parser = argparse.ArgumentParser()

class DefaultListAction(argparse.Action):
    CHOICES = ['clear','copy','dump','lock']
    def __call__(self, parser, namespace, values, option_string=None):
        if values:
            for value in values:
                if value not in self.CHOICES:
                    message = ("invalid choice: {0!r} (choose from {1})"
                               .format(value,
                                       ', '.join([repr(action)
                                                  for action in self.CHOICES])))

                    raise argparse.ArgumentError(self, message)
            setattr(namespace, self.dest, values)

parser.add_argument('actions', nargs='*', action=DefaultListAction,
                    default = ['dump', 'clear'],
                    metavar='ACTION')

print parser.parse_args([])
print parser.parse_args(['lock'])

Вывод сценария:

$ python test.py 
Namespace(actions=['dump', 'clear'])
Namespace(actions=['lock'])
4 голосов
/ 16 декабря 2011

В документации (http://docs.python.org/dev/library/argparse.html#default), сказано:

Для позиционных аргументов с наргами, равными? или *, значение по умолчанию используется при отсутствии аргумента командной строки.

Тогда, если мы сделаем:

acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', choices=acts, default='clear')    
print p.parse_args([])

Мы получаем то, что ожидаем

Namespace(action='clear')

Проблема в том, что вы выставляете список по умолчанию. Но я видел это в документе,

parser.add_argument('bar', nargs='*', default=[1, 2, 3], help='BAR!')

Итак, я не знаю: - (

Во всяком случае, вот обходной путь, который делает работу, которую вы хотите:

import sys, argparse
acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', choices=acts)
args = ['dump', 'clear'] # I set the default here ... 
if sys.argv[1:]:
    args = p.parse_args()
print args
3 голосов
/ 16 декабря 2011

Вы можете проверить, предоставляет ли пользователь действия (в этом случае он обрабатывает его как обязательный аргумент позиции) или не предоставляет никаких действий (в этом случае он по умолчанию анализируется как необязательный аргумент):

import argparse
import sys

acts = ['clear', 'copy', 'dump', 'lock']
p = argparse.ArgumentParser()
if sys.argv[1:]:
    p.add_argument('action', nargs = '*', choices = acts)
else:
    p.add_argument('--action', default = ['dump', 'clear'])

args = p.parse_args()
print(args)

при запуске выдает следующие результаты:

% test.py 
Namespace(action=['dump', 'clear'])
% test.py lock
Namespace(action=['lock'])
% test.py lock dump
Namespace(action=['lock', 'dump'])

Возможно, у вас есть и другие варианты для разбора. В этом случае вы можете использовать parse_known_args для анализа других параметров, а затем обработать аргументы unknown во втором проходе:

import argparse

acts = ['clear', 'copy', 'dump', 'lock']
p = argparse.ArgumentParser()
p.add_argument('--foo')
args, unknown = p.parse_known_args()
if unknown:
    p.add_argument('action', nargs = '*', choices = acts)
else:
    p.add_argument('--action', default = ['dump', 'clear'])

p.parse_args(unknown, namespace = args)
print(args)

при запуске выдает следующие результаты:

% test.py 
Namespace(action=['dump', 'clear'], foo=None)
% test.py --foo bar
Namespace(action=['dump', 'clear'], foo='bar')
% test.py lock dump
Namespace(action=['lock', 'dump'], foo=None)
% test.py lock dump --foo bar
Namespace(action=['lock', 'dump'], foo='bar')
1 голос

Я закончил тем, что сделал следующее:

  • нет append
  • добавьте пустой список к возможным вариантам, иначе пустые разрывы ввода
  • безdefault
  • затем проверить пустой список и установить фактическое значение по умолчанию в этом случае

Пример:

parser = argparse.ArgumentParser()
parser.add_argument(
    'is',
    type=int,
    choices=[[], 1, 2, 3],
    nargs='*',
)

args = parser.parse_args(['1', '3'])
assert args.a == [1, 3]

args = parser.parse_args([])
assert args.a == []
if args.a == []:
    args.a = [1, 2]

args = parser.parse_args(['1', '4'])
# Error: '4' is not valid.
1 голос
/ 16 декабря 2011

Действие добавлялось из-за параметра "action = 'append'", который вы передали в argparse.

После удаления этого параметра аргументы, переданные пользователем, будут отображаться самостоятельно, но программа выдаст ошибку, если аргументы не переданы.

Добавление префикса '-' к первому параметру решает эту проблему самым ленивым образом.

acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('--action', nargs='*', choices=acts, default=[['dump', 'clear']])
args = p.parse_args()

Недостатком этого подхода является то, что опциям, переданным пользователем, теперь должно предшествовать '--action', например:

app.py --action clear dump copy
...