导致 Python 的 argparse 执行默认操作

Posted

技术标签:

【中文标题】导致 Python 的 argparse 执行默认操作【英文标题】:Cause Python's argparse to execute action for default 【发布时间】:2014-03-02 06:22:19 【问题描述】:

我正在使用 argparse 的操作将各种数据添加到类中。如果命令行中未提供该 arg,我想对该默认值使用该操作。这可能吗? 谢谢!

【问题讨论】:

Python argparse: default value or specified value 的可能重复项 @LutzHorn,这不是我需要的(我不认为),因为我想 1:存储 argname=val 和 2:应用任意 argparse.Action 就好像 --argname= val 是在命令行中设置的 【参考方案1】:

argparse 在应用default 时不使用action。它只使用setattr。如果默认是字符串,它可以使用type。但是你可以直接调用action

这里我使用从文档中借来的自定义操作类。在第一个 parse_args 没有任何反应。然后我创建一个新的namespace,并调用默认的操作。然后我将该命名空间传递给parse_args。要理解这一点,您需要将其导入交互式 shell,并检查命名空间和操作的属性。

# sample custom action from docs
class FooAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        print('Setting: %r %r %r' % (namespace, values, option_string))
        setattr(namespace, self.dest, 'action:'+values)
p = argparse.ArgumentParser()
a1 = p.add_argument('--foo', action=FooAction, default='default')
print 'action:',a1
print p.parse_args([])

ns = argparse.Namespace()
a1(p, ns, a1.default, 'no string') # call action
print p.parse_args([],ns)
print p.parse_args(['--foo','1'],ns)

产生:

action: FooAction(option_strings=['--foo'], dest='foo', nargs=None, const=None, default='default', type=None, choices=None, help=None, metavar=None)
Namespace(foo='default')
Setting: Namespace() 'default' 'no string'
Namespace(foo='action:default')
Setting: Namespace(foo='action:default') '1' '--foo'
Namespace(foo='action:1')

我调整了输出以突出显示何时使用该操作。


这是一种对未在命令行中给出的参数执行特殊操作的方法(或使用值 == 给出的默认值)。这是https://***.com/a/24638908/901925 中给出的类的简化。

class Parser1:
    def __init__(self, desc):
        self.parser = argparse.ArgumentParser(description=desc)
        self.actions = []

    def milestone(self, help_='milestone for latest release.', default=None):
        action = self.parser.add_argument('-m', '--milestone', help=help_, default=default)
        self.actions.append(action)
        return self

    def parse(self):
        args = self.parser.parse_args()
        for a in self.actions:
            if getattr(args, a.dest) == a.default:
                print 'Please specify', a.dest
                values = raw_input('>')
                setattr(args, a.dest, values)
        return args

print Parser1('desc').milestone(default='PROMPT').parse()

提示在parse_args之后完成。我看不出有任何理由再次致电parse_args

【讨论】:

谢谢。这肯定是一种方法。 (此外,它为使用 argparse 的其他部分提供了很好的示例)。我最终将 FooAction.__call__ 的主体拉到另一个方法,并手动迭代像你的 a1.没有内置真的很可惜。 +1 - 当没有传入选项时,我用它来提示用户 - 它真的必须如此 hacky 吗?如果未设置选项,则无法通过 API 要求运行操作? 我会等到整个命令行被解析后再询问用户进一步的输入。最简单的方法是运行args=parse_args...,然后根据 `args.尝试做“我第一次没有遇到选择”的事情是没有意义的! @hpaulj:好吧,如果没有指定选项或没有选项是默认选项 - 这当然应该是可能的。包括@user_to_notify 让我收到通知 - 偶然看到这个。将发布我的代码,以便您了解我的意思 你的解决方案的关键是`p.add_argument`返回动作-为什么没有记录:docs.python.org/2.7/library/…?【参考方案2】:

如果未指定选项,我需要提示用户 - 我就是这样做的:

class _PromptUserAction(argparse.Action):

    def __call__(self, parser, namespace, values, option_string=None):
        if values == self.default:
            print 'Please specify', self.dest
            values = raw_input('>')
        setattr(namespace, self.dest, values)

class Parser:
    def __init__(self, desc, add_h=True):
        self.parser = argparse.ArgumentParser(description=desc, add_help=add_h,
                        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
        #actions to be run on options if not specified (using default to check)
        self.actions = []

    @staticmethod
    def new(description, add_help=True):
        return Parser(description, add_help)

    # ...

    def milestone(self, help_='Specify the milestone for latest release.'):
        action = self.parser.add_argument('-m', '--milestone',
                                          dest='milestone',
                                          action=_PromptUserAction,
                                          default='PROMPT', # needed I think
                                          type=str,
                                          help=help_)
        self.actions.append(action)
        return self

    def parse(self):
        """
        Return an object which can be used to get the arguments as in:
            parser_instance.parse().milestone

        :return: ArgumentParser
        """
        args = self.parser.parse_args()
        # see: http://***.com/a/21588198/281545
        dic = vars(args)
        ns = argparse.Namespace()
        for a in self.actions:
            if dic[a.dest] == a.default:
                a(self.parser, ns, a.default) # call action
        # duh - can I avoid it ?
        import sys
        return self.parser.parse_args(sys.argv[1:],ns)

如果无需重新解析 args(import sys 部分)就能以某种方式完成,我很感兴趣。也许argparse.Action 的一些构造函数选项?

【讨论】:

我已将您的示例的简化版本添加到我的答案中。这就是您正在寻找的功能吗? @hpaulj:pythonic - 谢谢 - 我坚持要尝试“运行我的行动” - 将在某个时候使用它并在我遇到任何奇怪时回发

以上是关于导致 Python 的 argparse 执行默认操作的主要内容,如果未能解决你的问题,请参考以下文章

Python 命令行工具 argparse 模块使用详解

Python argparse:默认参数存储为字符串,而不是列表

ArgParse Python 模块:更改继承参数的默认参数值

python ArgParse 自定义操作与使用 metavar 的键值选项导致 -h 输出中的重复

Python命令模块argparse学习笔记

如何通过批处理文件执行 Python 代码并传递参数 - 使用 argparse