2016-09-01 5 views
0

Я хочу собрать некоторые опции в качестве dict, используя argparse. Я написал собственный класс Action следующим образом. Проблема в том, что метод __call__ никогда не вызывается.Argparse - Custom Action update to dest dict

#!/usr/bin/env python3 

import argparse 

class UpdateDict(argparse.Action): 
    def __init__(self, option_strings, dest, nargs=None, **kwargs): 
     print('UpdateDict', option_strings, dest, nargs) 
     if nargs != 1: 
      raise ValueError("nargs must be 1") 
     super(UpdateDict, self).__init__(option_strings, dest, nargs=nargs, **kwargs) 
     print(self) 

    def __call__(self, parser, namespace, values, option_string=None): 
     print('%r %r %r' % (namespace, values, option_string)) 
     dest = getattr(namespace, self.dest) 
     print(dest) 
     key = option_string[1] 
     dest.update(key = values[0]) 

def parse_args(): 
    parser = argparse.ArgumentParser(description='formula alpha') 
    parser.add_argument('formula', nargs=1, type=str, help='alpha formula') 
    parser.set_defaults(mydict={}) 

    parser.add_argument('-a', '--alpha', nargs=1, type=str, default=['alas'], action=UpdateDict, dest='mydict') 
    parser.add_argument('-b', '--beta', nargs=1, type=int, default=[0], action=UpdateDict, dest='mydict') 

if __name__ == '__main__':                                               
    args = parse_args() 
    print(args) 

ответ

0

Ваш parse_args функции необходимо вернуть анализатор, который он создает, как вам нужно его метод parse_args для фактического анализа. Кроме того, вы должны указать аргументы из sys.argv[1:], в основном аргументы, за исключением аргумента главной программы (т. Е. Имя вашего скрипта). Поэтому вы должны переименовать parse_args в более подходящее имя. Неполный пример:

def make_parser(): 
    # your ArgumentParser construction code ... 
    return parser 

if __name__ == '__main__': 
    import sys 
    parser = make_parser() 
    parser.parse_args(sys.argv[1:]) 

Обратите внимание, что у вас есть некоторые незначительные проблемы с реализацией ваших действий, которые вам нужно будет исправить. Когда я бросил весь код (с return parser добавлено) на ArgumentParser, что ваш код построен, это был вопрос:

>>> parser.parse_known_args(['-b', '1']) 
Namespace(formula=None, mydict=['alas']) [1] '-b' 
['alas'] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python3.4/argparse.py", line 1769, in parse_known_args 
    namespace, args = self._parse_known_args(args, namespace) 
    File "/usr/lib/python3.4/argparse.py", line 1975, in _parse_known_args 
    start_index = consume_optional(start_index) 
    File "/usr/lib/python3.4/argparse.py", line 1915, in consume_optional 
    take_action(action, args, option_string) 
    File "/usr/lib/python3.4/argparse.py", line 1843, in take_action 
    action(self, namespace, argument_values, option_string) 
    File "<stdin>", line 13, in __call__ 
AttributeError: 'list' object has no attribute 'update' 
0

С вашим сценарием, упрощено, чтобы сосредоточиться на положить значения в mydict:

import argparse 

class UpdateDict(argparse.Action): 
    def __call__(self, parser, namespace, values, option_string=None): 
     adict = getattr(namespace, 'mydict') 
     adict.update({self.dest: values}) 

def parse_args(): 
    parser = argparse.ArgumentParser() 
    parser.set_defaults(mydict={}) 
    parser.add_argument('-a', '--alpha', default='alas', action=UpdateDict) 
    parser.add_argument('-b', '--beta', type=int, default=0, action=UpdateDict) 
    return parser.parse_args() # parse and return 

if __name__ == '__main__':                                               
    args = parse_args() 
    print(args) 

я

2333:~/mypy$ python3 stack39263156.py 
Namespace(alpha='alas', beta=0, mydict={}) 
2337:~/mypy$ python3 stack39263156.py -a one 
Namespace(alpha='alas', beta=0, mydict={'alpha': 'one'}) 
2337:~/mypy$ python3 stack39263156.py -a one -b 2 
Namespace(alpha='alas', beta=0, mydict={'beta': 2, 'alpha': 'one'}) 

вы можете восстановить тест nargs=1, если вы хотите, но по умолчанию None означает 1 аргумент. Большинство из них не используют nargs=1. Он гарантирует, что значение представляет собой единый список элементов; большинству людей требуется несколько списков элементов (например, «+» или «*» nargs).

Мне нравится использовать set_defaults, чтобы положить mydict в пространство имен. Затем я извлекаю его из пространства имен и обновляю его, используя аргумент dest как key. Ваша версия смущает dest и перезаписывает атрибут mydict.

По умолчанию store_action использует setattr(namespace, self.dest, values), чтобы сделать то же самое, но с объектом пространства имен, а не с встроенным mydict.

Обычная обработка по умолчанию заполняет атрибуты alpha и beta (dest). Для того, чтобы вкладывать их в mydict вместо этого, вы должны использовать что-то вроде

parser.set_defaults(mydict={'alpha':'alas', 'beta':0}) 

SUPPRESS может использоваться, чтобы сохранить настройки по умолчанию из обычных dest атрибутов

parser.add_argument('-a', '--alpha', action=UpdateDict, default=argparse.SUPPRESS) 
parser.add_argument('-b', '--beta', type=int, action=UpdateDict) 

без аргументов запуска будет производить:

2347:~/mypy$ python3 stack39263156.py 
Namespace(beta=None, mydict={'beta': 0, 'alpha': 'alas'}) 

Использование встроенного словаря - интересная идея, хотя я не уверен, что она добавляет. vars(args) рекомендуется как способ превратить namespace в словарь.

{'mydict': {'alpha': 'one', 'beta': 2}, 'beta': None}