Python Argparse:获取用于命名空间变量的命令行参数

Python Argparse: Get the command-line argument used for a Namespace variable

是否有适当的或至少更好的方法来获取使用哪个命令行参数来设置命名空间参数(属性)值?

我目前正在使用这样的东西:

>>> import argparse
>>>
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--do-a', '-a',
...     default=False, action='store_true',
...     dest='process_foo',
...     help="Do some awesome a to the thing.")
>>> args = parser.parse_args()
>>>
>>> def get_argument(parser, dest):
...     for action in parser._actions:
...         if action.dest == dest:
...             return action.option_strings[0], action.help
...     return dest, ''
...
>>> get_argument(parser, 'process_foo')
('--do-a', 'Do some awesome a to the thing.')

这可能适用于 99% 的情况;但是,如果不止一个命令行参数可以设置 process_foo,这将不起作用,并且访问 'hidden' 实例属性 (parser._actions) 充其量也是笨拙的。有更好的方法吗?

我将其添加到一个模块中,所有数据科学过程都继承了日志环境和其他内容,以便我们具有更好的重现性。有问题的模块已经自动记录设置、参数、命令行参数等,但在某些方面对用户来说不是很友好。

不用担心_actions的“隐蔽性”。这是存储对 add_argument 创建的所有 Actions 的引用的主要列表。您不应该 fiddle 使用该列表,但您当然可以使用它来收集信息。

add_argument 创建一个 Action 对象,将其放入 _actions(通过 _add_action 方法),并且 returns 它。如果您不喜欢使用 _actions,您可以使用 add_argument.

返回的对象收集您自己的引用列表

我从 _add_action 看到它还将标记的操作放在 self._option_string_actions 字典中,从而更容易将选项字符串与其 action.

配对

解析不会对 parser、其属性或 actions 进行任何更改。虽然它有各种局部变量(在 _parse_known_args 方法中),但唯一改变的是 args 命名空间。

它使对 args 的访问尽可能通用,包括 getattrsetattrhasattr。这包括在解析开始时设置默认值。解析器不维护哪个选项字符串触发特定 take_action 和后续 setattr 的记录。然而 Action__call__ 确实得到了字符串。对于最常见的 'store_action' 调用是

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

我认为所有定义的 Action 子类都使用 self.dest,但用户定义的子类不必使用。他们甚至可以设置其他名称空间属性,或 none(例如帮助不设置任何内容)。他们还可以记录 option_string.

也可以在不通过定义的操作的情况下设置命名空间属性。您的 dest 测试对这些没有帮助。

https://docs.python.org/3/library/argparse.html#parser-defaults

展示了如何在不在参数中定义属性的情况下设置它们。子命令显示了如何使用它来定义将与特定解析器一起使用的函数。

https://docs.python.org/3/library/argparse.html#the-namespace-object 还表明可以提供部分初始化的命名空间。

我建议创建您自己的操作 class 派生自 argarse.Action,它不仅会在命名空间中存储解析值,还会在命名空间中存储解析值的选项字符串。

完整的工作示例:

import argparse

class StoreTrueWithOptionStringAction(argparse.Action):
    def __init__(self,
                 option_strings,
                 dest,
                 default=None,
                 required=False,
                 help=None,
                 metavar=None):
        super().__init__(option_strings=option_strings,
                         dest=dest,
                         nargs=0,
                         const=True,
                         default=default,
                         required=required,
                         help=help)
    
    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, self.const)
        if option_string is not None:
            setattr(namespace, f'{self.dest}_option_string', option_string)

def get_parser():
    parser = argparse.ArgumentParser()
    parser.add_argument('--bar', action=StoreTrueWithOptionStringAction, dest='foo', default=False)
    parser.add_argument('--baz', action=StoreTrueWithOptionStringAction, dest='foo', default=False)
    return parser

def main():
    parser = get_parser()
    args = parser.parse_args()
    print(args.foo)
    print(args.foo_option_string)

if __name__ == '__main__':
    main()

输出:

$ python3 main.py --bar
True
--bar
$ python3 main.py --baz
True
--baz