Аргумент аргумента 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.