Четко передайте позиционные аргументы как аргументы и необязательные аргументы как kwargs от argpase к функции

Я бы хотел написать сценарий Python, который принимает некоторые необходимые позиционные и некоторые необязательные аргументы командной строки через argparse . Назовем позиционные args a , b и c и необязательные аргументы x , y и z . В моем скрипте Python я хотел бы передать их функции; в частности, я хочу, чтобы a , b и c передавались как *args , а x , y и z передавались как **kwargs , последние сохраняли свои имена. Я хотел бы делать это много раз с различными функциями и разными числами позиционных и необязательных аргументов. Есть ли опрятный, гибкий и / или питонический способ сделать это?

Вот пример кода:

 import argparse def parse(): parser = argparse.ArgumentParser() parser.add_argument('a', help='1st arg') parser.add_argument('b', help='2nd arg') parser.add_argument('c', help='3rd arg') parser.add_argument('-x', '--x', help='1st kwarg') parser.add_argument('-y', '--y', help='2nd kwarg') parser.add_argument('-z', '--z', help='3rd kwarg') return parser.parse_args() def func(*args, **kwargs): a, b, c = args print 'a=', a print 'b=', b print 'c=', c for k, v in kwargs.iteritems(): print '%s=' % k, v if __name__ == '__main__': all_args = parse() ### need to split all_args into args and kwargs here ### func(*args, **kwargs) 

Пространство имен, которое вы вернетесь из parse_args будет иметь атрибуты, соответствующие вашим аргументам. Не будет никакого различия между позиционными аргументами и вариантами, например

 args Namespace(a='1',b='one',x='foo', y=...) 

которые, как хорошо документированы, можно получить в виде

 args.a args.x etc 

Пространство имен также может быть преобразовано в словарь

 vars(args) {'a'='1', 'b'='one', etc} 

Вы можете передать словарь в функцию как **kwargs . Это стандартная практика аргументов Python.

Если вы хотите передать некоторые аргументы как *args вам придется разделить их с пространством имен или словаря самостоятельно. Ничто в argparse не сделает этого для вас.

Вы можете написать такую ​​функцию, как (не тестировалось):

 def split_args(args): vargs = vars(args) alist = ['a','b','c'] args1 = [] for a in alist: v = vargs.pop(a) args1.append(v) return args1, vars 

Или более компактно, поместите pop в понимание списка:

 In [702]: vargs = dict(a=1,b=3,c=4,x=5,y=3,z=3) In [703]: [vargs.pop(a) for a in ['a','b','c']] Out[703]: [1, 3, 4] In [704]: vargs Out[704]: {'y': 3, 'x': 5, 'z': 3} In [705]: def foo(*args,**kwargs): .....: print(args) .....: print(kwargs) .....: In [706]: vargs = dict(a=1,b=3,c=4,x=5,y=3,z=3) In [707]: foo(*[vargs.pop(a) for a in ['a','b','c']],**vargs) (1, 3, 4) {'x': 5, 'z': 3, 'y': 3} 

Дальнейшая идея с использованием пользовательского класса Action

parser определяет, является ли аргумент optional v- positional его атрибутом option_strings . add_argument возвращает подкласс Action , который будет иметь такие атрибуты, как:

 MyAction(option_strings=[], dest='baz', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None) 

Это positional потому что option_strings – пустой список.

 MyAction(option_strings=['-m', '--mew'], dest='mew', nargs=None,...) 

является optional потому что этот список не пуст.

Парсер соответствует строкам ввода с параметрами option_strings и nargs , а затем передает значения методу __call__ для соответствующего Action . Этот метод имеет определение типа:

 def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, values) 

Это действие store по умолчанию. Значения помещаются в namespace как атрибут dest .

Параметр option_string – это строка, option_string этот вызов, что-то вроде «-m» или «–mew» или None для positional . Определенные типы действий не используют это, но определенный пользователем класс Action может что-то сделать.

 class MyAction(argparse._StoreAction): def __call__(self, parser, namespace, values, option_string=None): # store option_string along with values in the namespace setattr(namespace, self.dest, [values,option_string]) 

Или вы можете сделать что-то особенное с positionals , например

  if option_string is None: # append values to a `star_args` attribute # rather than self.dest 

С действием, подобным этому, positionals могут быть доступны после разбора:

 args.star_args 

Парсер действительно поддерживает атрибут списка, подобный этому. Дополнительные extras , возвращаемые parse_known_args , временно сохраняются в пространстве имен в атрибуте «_UNRECOGNIZED_ARGS_ATTR».