Несколько позиционных аргументов с Python и argparse - PullRequest
22 голосов
/ 21 марта 2011

Я пытаюсь использовать 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, где написано так или иначе, если это возможно, хотя вполне возможно (вероятно?), Я что-то упустил.У кого-нибудь есть предложения?

Ответы [ 4 ]

18 голосов
/ 21 марта 2011

Таким способом нельзя чередовать переключатели (т. Е. -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'])
10 голосов
/ 21 марта 2011

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'])

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

3 голосов
/ 31 июля 2013

Действие 'добавить' имеет больше смысла с дополнительным:

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'])
2 голосов
/ 06 июля 2017

Мне кажется, что 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 «опции» доступны в опциях, а список возможных перемежающихся других «аргументов» в аргументах.

...