如何子类化 argparse.Action 以添加自定义操作?

How do I subclass argparse.Action to add a custom action?

我有一个命令行脚本,我正在尝试 运行 如果该值不存在,它会将默认值插入到命名空间中,或者如果存在则按原样使用提供的参数。

所以我想这样做:

myscript.py --merge

将导致参数解析器命名空间如下所示:

Namespace(merge='--merge')

否则,如果我调用

myscript.py

命名空间应如下所示:

Namespace(merge='DONTMERGE')

我想我需要子class argparse.Action class 的 __call__ 方法来执行此处指定的自定义操作:https://pymotw.com/2/argparse/ 但是我不知道该怎么做。

我认为这样的事情可以解决问题:

class CustomAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        if isinstance(self.values, None):
            self.values = 'NOMERGE'
        else:
            self.values = '--nomerge'
        setattr(namespace, self.dest, values)

很遗憾,我没有得到预期的结果。

我认为你只需要一个正常的 store_const 参数。

parser.add_argument('--merge', action='store_const', const='MERGE', default='DONTMERGE')

如果您使用 --merge 调用脚本,merge 参数采用值 MERGE(上面指定为 const)。否则,merge 参数采用值 DONTMERGE(上面指定为 default)。

https://docs.python.org/2/library/argparse.html#action

您要求的确切内容不可能通过子类化 Action 来实现。原因是 CustomAction 实例被 __call__ 编辑 当且仅当 --merge 传递给参数解析器:

class CustomAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        print("Yay, I've been called")
        setattr(namespace, self.dest, values)


p = argparse.ArgumentParser()
p.add_argument('--merge', nargs='?', action=CustomAction)

print(p.parse_args([]))
print(p.parse_args(['--merge']))
Namespace(merge=None)
Yay, I've been called
Namespace(merge=None)

这里我必须传递 nargs='?',否则 argparse 需要 --merge.

的参数

许多 Action 覆盖 Action.__init__(...),但它无法访问解析器的命名空间 ¯\_(ツ)_/¯

如果你想完全使用子类,你可能必须对 ArgumentParser 本身进行子类化并修改 add_argument() 方法。

虽然可以用 Actions 做一些很酷的 and/or 奇怪的事情:

class ReverseAction(argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        setattr(namespace, self.dest, ''.join(reversed(values)))


p = argparse.ArgumentParser()
p.add_argument('--string', action=ReverseAction)

print(p.parse_args(['--string', '12345']))
Namespace(string='54321')

(我唯一一次自己创建argparse.Action的子类是为了实现高级验证)


至于你的具体情况,在我看来这只是一份简单的工作 action='store_true':

>>> parser.add_argument('--merge', action='store_true')
_StoreTrueAction(option_strings=['--merge'], dest='merge', nargs=0, const=True,
default=False, type=None, choices=None, help=None, metavar=None)
>>> parser.parse_args(['--merge'])
Namespace(merge=True)
>>> parser.parse_args([])
Namespace(merge=False)

…甚至 BooleanOptionalAction(在 Python >=3.9 中):

>>> parser.add_argument('--merge', action=argparse.BooleanOptionalAction, default=False)
BooleanOptionalAction(option_strings=['--merge', '--no-merge'], dest='merge', nargs=0,
const=None, default=False, type=None, choices=None, help=None, metavar=None)
>>> parser.parse_args([])
Namespace(merge=False)
>>> parser.parse_args(['--no-merge'])
Namespace(merge=False)
>>> parser.parse_args(['--merge'])
Namespace(merge=True)

照例看argparse docs and/or源代码=)