【发布时间】:2022-08-19 02:53:55
【问题描述】:
我的用例是多个可选位置参数,取自一组受限的choices,default 值是一个包含其中两个选项的列表。由于向后兼容性问题,我无法更改界面。我还必须保持与 Python 3.4 的兼容性。
这是我的代码。您可以看到我希望我的默认值是choices 集中的两个值的列表。
parser = argparse.ArgumentParser()
parser.add_argument(\'tests\', nargs=\'*\', choices=[\'a\', \'b\', \'c\', \'d\'],
default=[\'a\', \'d\'])
args = parser.parse_args()
print(args.tests)
所有这些都是正确的:
$ ./test.py a
[\'a\']
$ ./test.py a d
[\'a\', \'d\']
$ ./test.py a e
usage: test.py [-h] [{a,b,c,d} ...]
test.py: error: argument tests: invalid choice: \'e\' (choose from \'a\', \'b\', \'c\', \'d\')
这是不正确的:
$ ./test.py
usage: test.py [-h] [{a,b,c,d} ...]
test.py: error: argument tests: invalid choice: [\'a\', \'d\'] (choose from \'a\', \'b\', \'c\', \'d\')
我发现了很多类似的问题,但没有一个可以解决这个特定的用例。我发现的最有希望的建议(在不同的上下文中)是编写一个自定义操作并使用它而不是choices:
那并不理想。我希望有人能指出我错过的一个选项。
如果没有,这是我计划使用的解决方法:
parser.add_argument(\'tests\', nargs=\'*\',
choices=[\'a\', \'b\', \'c\', \'d\', \'default\'],
default=\'default\')
只要我保持向后兼容性,我就可以添加参数。
谢谢!
更新:我最终选择了自定义操作。我很抗拒,因为这感觉不像是一个需要自定义任何东西的用例。然而,它似乎或多或少是子类化argparse.Action 的预期用例,它使意图非常明确,并给出了我发现的最干净的面向用户的结果。
class TestsArgAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
all_tests = [\'a\', \'b\', \'c\', \'d\']
default_tests = [\'a\', \'d\']
if not values:
setattr(namespace, self.dest, default_tests)
return
# If no argument is specified, the default gets passed as a
# string \'default\' instead of as a list [\'default\']. Probably
# a bug in argparse. The below gives us a list.
if not isinstance(values, list):
values = [values]
tests = set(values)
# If \'all\', is found, replace it with the tests it represents.
# For reasons of compatibility, \'all\' does not actually include
# one of the tests (let\'s call it \'e\'). So we can\'t just do
# tests = all_tests.
try:
tests.remove(\'all\')
tests.update(set(all_tests))
except KeyError:
pass
# Same for \'default\'
try:
tests.remove(\'default\')
tests.update(set(default_tests))
except KeyError:
pass
setattr(namespace, self.dest, sorted(list(tests)))
-
另一个最近的选项和
*nargs(不同的默认值),但除此之外相同点 - stackoverflow.com/questions/73205632/…。鉴于choices的处理方式,没有简单的方法可以完成这项工作。 -
这是一个有趣的方法。 (似乎
enumerate是不必要的,因为i未使用)。我可能最终会使用它,因为无论如何我都可能会覆盖使用消息。唯一的缺点是,如果有人多次指定一个参数,它会中断。在我的用例中,他们没有理由这样做,但我更愿意宽容。我可能可以使用*而不是?,我认为使用覆盖会很好。
标签: python parsing arguments argparse python-3.4