Argparse - 自定义操作更新到dest dict

时间:2016-09-01 05:15:42

标签: python argparse

我想使用argparse收集一些选项作为词典。我已经编写了一个自定义Action类,如下所示。问题是永远不会调用__call__方法。

#!/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)

2 个答案:

答案 0 :(得分: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'

答案 1 :(得分: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。它确保值是单个元素列表;大多数人都想要多个元素列表(例如&#39; +&#39;或&#39; *&#39; nargs)。

我喜欢您使用set_defaultsmydict放入命名空间。然后我从命名空间中检索它,并使用参数dest作为key来更新它。您的版本会混淆dest,并覆盖mydict属性。

默认store_action使用setattr(namespace, self.dest, values)来执行相同的操作,但使用命名空间对象而不是嵌入式mydict

通常的默认处理会填充alphabeta属性(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}