让我们想象一下,我有一个带有nargs="*"
参数--list
的CLI。我想过滤掉这个列表中的某些元素。
例如,如果用户输入了--list foo bar foo baz
,我想过滤掉所有的values == "foo"
,以便最终列表变成["bar", "baz"]
:
parser = argparse.ArgumentParser()
parser.add_argument(
"--list",
nargs="*",
)
parser.parse_args(["--list", "foo", "bar", "foo", "baz"])
很明显,我可以在生成的args的后处理步骤中解决这个问题。然而,我想知道argparse中是否有一个内置机制,可以在不进行后期处理的情况下实现这样的过滤器。
我尝试使用type=lambda: ...
,但它似乎只对值本身调用,而不是对最终列表调用,所以我想我不能用它来过滤值?
argparse.add_argument
有一个参数action
。来自文档:
ArgumentParser对象将命令行参数与操作相关联。尽管大多数操作只是向parse_args((返回的对象添加一个属性,但这些操作可以通过与之相关的命令行参数执行任何操作。
所以我们可以用action
进行过滤。文件说明:
创建自定义操作的推荐方法是扩展
Action
,覆盖__call__
方法以及可选的__init__
和format_usage
方法。
过滤argparse.Action
可能如下所示:
import argparse
from typing import Optional, Callable
class FilterNargsAction(argparse.Action):
def __init__(
self,
option_strings,
dest,
nargs: Optional[str] = None,
condition: Optional[Callable] = None,
**kwargs
):
if nargs not in ("*", "+"):
# We could also allow integers
raise ValueError("nargs must be one of (*, +)")
if condition is None:
raise ValueError("condition needs to be set")
self.condition = condition
super().__init__(option_strings, dest, nargs=nargs, **kwargs)
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, [v for v in values if self.condition(v)])
parser = argparse.ArgumentParser()
parser.add_argument(
"--list",
nargs="*",
action=FilterNargsAction,
condition=lambda x: x != "foo",
)
args = parser.parse_args(["--list", "foo", "bar", "foo", "baz"])
print(args.list)
如果在解析时绝对需要过滤参数,则可以使用自定义action
。