Аргумент аргумента argparse

У меня небольшая проблема.

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

Чтобы иметь аргументы, я делаю:

p_args = parser.parse_args(argv)
args = dict(p_args._get_kwargs())

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

Итак, есть ли возможность иметь аргументы в кортеже/списке/упорядоченном dict своим порядком в командной строке?

Ответы

Ответ 1

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

import argparse
class CustomAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        if not 'ordered_args' in namespace:
            setattr(namespace, 'ordered_args', [])
        previous = namespace.ordered_args
        previous.append((self.dest, values))
        setattr(namespace, 'ordered_args', previous)
parser = argparse.ArgumentParser()
parser.add_argument('--test1', action=CustomAction)
parser.add_argument('--test2', action=CustomAction)

Чтобы использовать его, например:

>>> parser.parse_args(['--test2', '2', '--test1', '1'])
Namespace(ordered_args=[('test2', '2'), ('test1', '1')], test1=None, test2=None)

Ответ 2

Если вам нужно знать порядок, в котором аргументы появляются в вашем синтаксическом анализаторе, вы можете настроить парсер следующим образом:

import argparse

parser = argparse.ArgumentParser(description = "A cool application.")
parser.add_argument('--optional1')
parser.add_argument('positionals', nargs='+')
parser.add_argument('--optional2')

args = parser.parse_args()
print args.positionals

Вот быстрый пример запуска этого кода:

$ python s.py --optional1 X --optional2 Y 1 2 3 4 5
['1', '2', '3', '4', '5']

Обратите внимание, что args.positionals - это список с позиционными аргументами в порядке. Дополнительную информацию см. В документации argparse.

Ответ 3

Это немного хрупко, поскольку он полагается на понимание внутренних элементов argparse.ArgumentParser, но вместо перезаписывания argparse.ArgumentParser.parse_known_args, вот что я использую:

class OrderedNamespace(argparse.Namespace):
    def __init__(self, **kwargs):
        self.__dict__["_arg_order"] = []
        self.__dict__["_arg_order_first_time_through"] = True
        argparse.Namespace.__init__(self, **kwargs)

    def __setattr__(self, name, value):
        #print("Setting %s -> %s" % (name, value))
        self.__dict__[name] = value
        if name in self._arg_order and hasattr(self, "_arg_order_first_time_through"):
            self.__dict__["_arg_order"] = []
            delattr(self, "_arg_order_first_time_through")
        self.__dict__["_arg_order"].append(name)

    def _finalize(self):
        if hasattr(self, "_arg_order_first_time_through"):
            self.__dict__["_arg_order"] = []
            delattr(self, "_arg_order_first_time_through")

    def _latest_of(self, k1, k2):
        try:
            print self._arg_order
            if self._arg_order.index(k1) > self._arg_order.index(k2):
                return k1
        except ValueError:
            if k1 in self._arg_order:
                return k1
        return k2

Это работает благодаря знанию, что argparse.ArgumentParser.parse_known_args запускается через весь список опций после установки значений по умолчанию для каждого аргумента. Это означает, что пользовательские аргументы начинаются с первого раза, когда setattr показывает аргумент, который он видел раньше.

Использование:

options, extra_args = parser.parse_known_args(sys.argv, namespace=OrderedNamespace())

Вы можете проверить options._arg_order для порядка заданных пользователем аргументов командной строки или использовать options._latest_of("arg1", "arg2"), чтобы узнать, какой из -arg1 или -arg2 был указан позже в командной строке (что для моих целей было тем, что Мне нужно: посмотреть, какой из двух вариантов будет основным).

UPDATE: пришлось добавить метод _finalize для обработки патологического случая sys.argv(), не содержащего никаких аргументов в списке)

Ответ 4

Для этого используется специально разработанный модуль:

https://github.com/claylabs/ordered-keyword-args

без использования модуля orderkwargs

def multiple_kwarguments(first , **lotsofothers):
    print first

    for i,other in lotsofothers:
         print other
    return True

multiple_kwarguments("first", second="second", third="third" ,fourth="fourth" ,fifth="fifth")

вывод:

first
second
fifth
fourth
third

При использовании модуля orderkwargs

from orderedkwargs import ordered kwargs  
@orderedkwargs  
def mutliple_kwarguments(first , *lotsofothers):
    print first

    for i, other in lotsofothers:
        print other
    return True


mutliple_kwarguments("first", second="second", third="third" ,fourth="fourth" ,fifth="fifth")

Вывод:

first
second
third
fourth
fifth

Примечание. При использовании этого модуля с декоратором над функцией требуется одиночный астерик.

Ответ 5

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

Пользовательский класс действий просто плоский не работал у меня. У меня были другие аргументы, которые использовали другое действие, такое как аргументы 'store_true' и default, также не работают, поскольку пользовательский класс действия не вызывается, если аргумент не указан в командной строке. Что для меня работало, так это создание класса-оболочки:

import collections

from argparse import ArgumentParser

class SortedArgumentParser():
    def __init__(self, *args, **kwargs):
        self.ap = ArgumentParser(*args, **kwargs)
        self.args_dict = collections.OrderedDict()      

    def add_argument(self, *args, **kwargs):
        self.ap.add_argument(*args, **kwargs)
        # Also store dest kwarg
        self.args_dict[kwargs['dest']] = None

    def parse_args(self):
        # Returns a sorted dictionary
        unsorted_dict = self.ap.parse_args().__dict__
        for unsorted_entry in unsorted_dict:
            self.args_dict[unsorted_entry] = unsorted_dict[unsorted_entry]

        return self.args_dict

Плюсы в том, что метод add_argument должен иметь ту же функциональность, что и исходный ArgumentParser. Недостатки заключаются в том, что если вам нужны другие методы, вам придется написать их для всех. К счастью для меня все, что я когда-либо использовал, было add_argument и parse_args, так что это послужило моим целям довольно хорошо. Вам также нужно будет сделать больше работы, если вы хотите использовать родительский ArgumentParser s.