порядковый аргумент argparse - PullRequest
16 голосов
/ 27 января 2012

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

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

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

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

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

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

Ответы [ 5 ]

14 голосов
/ 27 января 2012

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

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)
4 голосов
/ 29 февраля 2012

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

ОБНОВЛЕНИЕ: пришлось добавить _finalize метод для обработки патологического случая sys.argv(), не содержащего аргументов в списке)

4 голосов
/ 27 января 2012

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

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 документацию для получения дополнительной информации.

1 голос
/ 18 июля 2016

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

У меня просто не работает класс пользовательских действий. У меня были другие аргументы, в которых использовалось другое действие, например '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.

1 голос
/ 03 октября 2014

Специально для этого предусмотрен модуль:

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

без использования упорядоченного модуля kwargs

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

При использовании упорядоченного модуля kwargs

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

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

...