使用 argparse 处理一组独占/必需参数的最佳方法?

Best way to handle a group of arguments being exclusive/required with argparse?

提问人:M. G 提问时间:6/21/2023 更新时间:7/20/2023 访问量:39

问:

所以我有四个论点。--A, --B, --C, --D. --A 被认为是“单独”的,而 --B、--C 和 --D 可以一起放在一个组中。如果 B/C/D 中的一个存在,那么 B/C/D 的其余部分也可以被认为是必需的(相互排斥的组?

此外,我希望它要么是必需的 --A 要么是必需的,要么是 BCD 组是必需的,而不是两者都是必需的。当一个不存在时,另一个是必需的。我一直在使用互斥组和必需参数的各种组合,但不断遇到错误。

我发现处理这个问题的大多数方法最终都是使用“必需”标志和一个单独的函数或奇怪的条件,但我想知道是否有一种好方法可以用 argparse 本身的工具处理这个问题。对于每个 BCD,我能够使用“required = '--A' not in sys.argv”来做到这一点,但宁愿与放在一起的参数一起处理它,而不是查看通过 sys 传递的文本,如果可以的话。在 parser.parse_known_args()[0] 中没有 “required = '--A']。每个 BCD 的 dict“似乎也有效,但对于应该能够在 argparse 工具中处理的事情来说,这似乎是一个奇怪的解决方案。任何帮助都是值得赞赏的!

python 参数 参数传递 argparse 必需

评论

0赞 Scott Hunter 6/21/2023
有没有办法将 B & C & D 的值组合成一个东西,这样你就可以为它们使用一个标志(例如,BCD)?
0赞 hpaulj 6/21/2023
不,没有提供对分组参数进行花哨组合的方法。有 obvioius mutually_exclusive_group,但这是一组参数中的简单 XOR。其中没有组的“嵌套”。但是,如果默认值全部,那么在解析是否提供了某些组合后,很容易进行测试。或者给所有参数提供有意义的默认值,在这种情况下,你不会关心它们是否已经提供。argparseNone
0赞 M. G 6/21/2023
并非如此,BCD 也会有与自身相关的单个参数。即便如此,我想我可以把它们包起来,但是在使用程序的上下文中,我需要保持命令行参数与原来的相同。
0赞 hpaulj 6/21/2023
请记住,在设计解析时,您必须向最终用户解释它。 设置为使用简单的 XOR 显示一个,但没什么花哨的。Remmber,您的主要目标是弄清楚最终用户想要什么。argparseusage

答:

0赞 Hai Vu 7/20/2023 #1

这是一个复杂的要求。我尝试了不同的方法,但没有一种像 M.G. 发现的那样完美。但是,如果我们自己去处理验证而不是让 argparse 来做,它可能会更简单。

#!/usr/bin/env python3
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--A")
parser.add_argument("--B")
parser.add_argument("--C")
parser.add_argument("--D")

options = parser.parse_args()
print(options)

# Validate: A XOR (B AND C AND D)
if options.A and any([options.B, options.C, options.D]):
    raise SystemExit("--A is present, cannot have --B, --C, and --D")
if options.A is None and not all([options.B, options.C, options.D]):
    raise SystemExit("--B, --C, --D must be together and all required")

示例运行:

# --B on its own
$ python3 main.py --B boy
Namespace(A=None, B='boy', C=None, D=None)
--B, --C, --D must be together and all required

# Missing --D
$ python3 main.py --B boy --C charlie                    
Namespace(A=None, B='boy', C='charlie', D=None)
--B, --C, --D must be together and all required

# All are present
$ python3 main.py --B boy --C charlie --D delta --A apple
Namespace(A='apple', B='boy', C='charlie', D='delta')
--A is present, cannot have --B, --C, and --D

# Happy path: --A
$ python3 main.py --A apple        
Namespace(A='apple', B=None, C=None, D=None)

# Happy path: --B, --C, and --D
$ python3 main.py --B boy --C charlie --D delta          
Namespace(A=None, B='boy', C='charlie', D='delta')