argparse
将可选参数中的破折号替换为下划线以确定其目的地:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--use-unicorns', action='store_true')
args = parser.parse_args(['--use-unicorns'])
print(args) # returns: Namespace(use_unicorns=True)
但是用户必须记住该选项是
--use-unicorns
还是--use_unicorns
;使用错误的变体会引发错误。
这可能会导致一些挫败感,因为代码中的变量
args.use_unicorns
并没有明确定义了哪个变体。
如何使
argparse
接受 --use-unicorns
和 --use_unicorns
作为定义此可选参数的有效方法?
parser.add_argument
接受多个标志作为参数(链接到文档)。让解析器接受这两种变体的一种简单方法是将参数声明为
parser.add_argument('--use-unicorns', '--use_unicorns', action='store_true')
然而,这两个选项都会显示在帮助中,而且它不是很优雅,因为它迫使人们手动编写变体。
另一种方法是子类
argparse.ArgumentParser
以使匹配不变,用下划线替换破折号。这需要一点点摆弄,因为 argparse_ActionsContainer._parse_optional
和 argparse_ActionsContainer._get_option_tuples
都必须修改才能处理这种匹配和缩写,例如--use_unic
。
我最终得到了以下子类方法,其中缩写的匹配从
_parse_optional
委托给 _get_option_tuples
:
from gettext import gettext as _
import argparse
class ArgumentParser(argparse.ArgumentParser):
def _parse_optional(self, arg_string):
# if it's an empty string, it was meant to be a positional
if not arg_string:
return None
# if it doesn't start with a prefix, it was meant to be positional
if not arg_string[0] in self.prefix_chars:
return None
# if it's just a single character, it was meant to be positional
if len(arg_string) == 1:
return None
option_tuples = self._get_option_tuples(arg_string)
# if multiple actions match, the option string was ambiguous
if len(option_tuples) > 1:
options = ', '.join([option_string
for action, option_string, explicit_arg in option_tuples])
args = {'option': arg_string, 'matches': options}
msg = _('ambiguous option: %(option)s could match %(matches)s')
self.error(msg % args)
# if exactly one action matched, this segmentation is good,
# so return the parsed action
elif len(option_tuples) == 1:
option_tuple, = option_tuples
return option_tuple
# if it was not found as an option, but it looks like a negative
# number, it was meant to be positional
# unless there are negative-number-like options
if self._negative_number_matcher.match(arg_string):
if not self._has_negative_number_optionals:
return None
# if it contains a space, it was meant to be a positional
if ' ' in arg_string:
return None
# it was meant to be an optional but there is no such option
# in this parser (though it might be a valid option in a subparser)
return None, arg_string, None
def _get_option_tuples(self, option_string):
result = []
if '=' in option_string:
option_prefix, explicit_arg = option_string.split('=', 1)
else:
option_prefix = option_string
explicit_arg = None
if option_prefix in self._option_string_actions:
action = self._option_string_actions[option_prefix]
tup = action, option_prefix, explicit_arg
result.append(tup)
else: # imperfect match
chars = self.prefix_chars
if option_string[0] in chars and option_string[1] not in chars:
# short option: if single character, can be concatenated with arguments
short_option_prefix = option_string[:2]
short_explicit_arg = option_string[2:]
if short_option_prefix in self._option_string_actions:
action = self._option_string_actions[short_option_prefix]
tup = action, short_option_prefix, short_explicit_arg
result.append(tup)
underscored = {k.replace('-', '_'): k for k in self._option_string_actions}
option_prefix = option_prefix.replace('-', '_')
if option_prefix in underscored:
action = self._option_string_actions[underscored[option_prefix]]
tup = action, underscored[option_prefix], explicit_arg
result.append(tup)
elif self.allow_abbrev:
for option_string in underscored:
if option_string.startswith(option_prefix):
action = self._option_string_actions[underscored[option_string]]
tup = action, underscored[option_string], explicit_arg
result.append(tup)
# return the collected option tuples
return result
其中很多代码直接源自
argparse
中的相应方法(来自此处的 CPython 实现)。使用此子类应该使可选参数的匹配与使用破折号 -
或下划线 _
保持不变。
parser.add_argument('--use-unicorns', action='store_true')
args = parser.parse_args(['--use-unicorns'])
print(args) # returns: Namespace(use_unicorns=True)
argparse
将 '-' 转换为 '_',因为在标志中使用 '-' 是公认的 POSIX 实践。但 args.use-unicones
是 Python 不可接受的。换句话说,它进行翻译,因此 dest
将是有效的 Python 变量或属性名称。
请注意,
argparse
不与 positionals
一起执行此翻译。在这种情况下,程序员可以完全控制 dest
参数,并且可以选择任何方便的内容。由于 argparse
在访问 getattr
时仅使用 setattr
和 Namespace
,因此对有效 dest
的约束很小。
有两个用户。这是你,程序员,还有你的最终用户。对你来说方便的东西可能对其他人来说并不是最佳的。
您还可以通过定义
dest
来指定 optional
。 metavar
可让您进一步控制 help
显示。
执行“-”替换的是
parser._get_optional_kwargs
:
if dest is None:
....
dest = dest.replace('-', '_')
另一种选择是以声明方式定义参数:
import argparse
def parse_args(argv):
arguments = [
{"args": ["--use_unicorns"], "action": "store_true"},
{"args": ["--use_rainbows"], "action": "store_true"},
# ...
]
parser = argparse.ArgumentParser()
for argument in arguments:
h_args = [x.replace("_", "-") for x in argument["args"]]
argument["args"].extend(x for x in h_args if x not in argument["args"])
kwargs = {k: v for k, v in argument.items() if k != "args"}
parser.add_argument(*argument["args"], **kwargs)
return parser.parse_args(argv)
print(parse_args(["--use-unicorns"]))