Подтвердить что ты не робот

Несколько позиционных аргументов с Python и argparse

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

>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-a', action='store_true')
>>> parser.add_argument('-b', action='store_true')
>>> parser.add_argument('input', action='append')
>>> parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
usage: ipython [-h] [-a] [-b] input
ipython: error: unrecognized arguments: filetwo filethree

Мне бы хотелось, чтобы это привело к пространству имен (a=True, b=True, input=['fileone', 'filetwo', 'filethree']), но не может понять, как это сделать - если это действительно возможно. Я не вижу ничего в документах или Google, которые говорят так или иначе, если это возможно, хотя вполне возможно (вероятно?) Я что-то упустил. У кого-нибудь есть предложения?

4b9b3361

Ответ 1

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

Вы можете использовать настраиваемое действие:

class MyAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):

        # Set optional arguments to True or False
        if option_string:
            attr = True if values else False
            setattr(namespace, self.dest, attr)

        # Modify value of "input" in the namespace
        if hasattr(namespace, 'input'):
            current_values = getattr(namespace, 'input')
            try:
                current_values.extend(values)
            except AttributeError:
                current_values = values
            finally:
                setattr(namespace, 'input', current_values)
        else:
            setattr(namespace, 'input', values)

parser = argparse.ArgumentParser()
parser.add_argument('-a', nargs='+', action=MyAction)
parser.add_argument('-b', nargs='+', action=MyAction)
parser.add_argument('input', nargs='+', action=MyAction)

И вот что вы получаете:

>>> parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])

Или вы можете изменить приведенное пространство имен следующим образом:

>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('-a', nargs='+')
>>> parser.add_argument('-b', nargs='+')
>>> parser.add_argument('input', nargs='+')
>>> result = parser.parse_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])

>>> inputs = []
>>> inputs.extend(result.a)
>>> inputs.extend(result.b)
>>> inputs.extend(result.input)

>>> modified = argparse.Namespace(
        a = result.a != [],
        b = result.b != [],
        input = inputs)

И вот что вы получаете:

>>> modified
Namespace(a=True, b=True, input=['filetwo', 'filethree', 'fileone'])

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

Ответ 2

Вы не можете чередовать переключатели (т.е. -a и -b) с помощью позиционных аргументов (т.е. fileone, filetwo и filethree) таким образом. Коммутаторы должны отображаться до или после позиционных аргументов, а не между ними.

Кроме того, чтобы иметь несколько позиционных аргументов, вам нужно указать параметр nargs на add_argument. Например:

parser.add_argument('input', nargs='+')

Это говорит argparse использовать один или несколько позиционных аргументов и добавлять их в список. Дополнительную информацию см. В документации argparse. С этой строкой код:

parser.parse_args(['-a', '-b', 'fileone', 'filetwo', 'filethree'])

приводит к:

Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])

Ответ 3

Действие "append" имеет больше смысла с опцией:

parser.add_argument('-i', '--input',action='append')
parser.parse_args(['-i','fileone', '-a', '-i','filetwo', '-b', '-i','filethree'])

Вы можете чередовать опции с отдельными позициями ( "input1 -a input2 -b input3" ), но вы не можете чередовать опции в одном многопозиционном позиционном. Но вы можете выполнить это с помощью двухэтапного анализа.

import argparse
parser1 = argparse.ArgumentParser()
parser1.add_argument('-a', action='store_true')
parser1.add_argument('-b', action='store_true')
parser2 = argparse.ArgumentParser()
parser2.add_argument('input', nargs='*')

ns, rest = parser1.parse_known_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
# Namespace(a=True, b=True), ['fileone', 'filetwo', 'filethree']

ns = parser2.parse_args(rest, ns)
# Namespace(a=True, b=True, input=['fileone', 'filetwo', 'filethree'])

http://bugs.python.org/issue14191 - предлагаемый патч, который будет делать это с помощью одного вызова:

parser.parse_intermixed_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])

Ответ 4

Мне кажется, что hpaulj на правильном пути, но делает вещи немного сложнее, чем необходимо. Я подозреваю, что Блэр ищет нечто похожее на поведение старого модуля optparse и на самом деле не нуждается в списке входных аргументов в поле ввода объекта args. Он просто хочет

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-a', action='store_true')
parser.add_argument('-b', action='store_true')
opts, args = parser.parse_known_args(['fileone', '-a', 'filetwo', '-b', 'filethree'])
# Namespace(a=True, b=True), ['fileone', 'filetwo', 'filethree']

В языке optparse "опции" доступны в опциях, а список возможных вкрапленных других "аргументов" - в args.