python argparse 字符串列表的选择接受唯一的部分列表元素

python argparse choices of string list accept unique partial list element

我想要以下规则

parser.add_argument('move', choices=['rock', 'paper', 'scissors'])

如果您传递字符的唯一子集,也可以工作(例如 "k" 或 "oc" 将被接受为 "rock" 但不会 "r" 因为它不是唯一的) .

我的需要是能够 运行 以尽可能最快的方式使用一个或多个参数来编写脚本,从而避免在子集足以让脚本理解选择时写入整个参数名称。

有没有办法仍然利用自动集成在帮助和错误处理中的便捷选择列表来获得此结果?

您可以定义 list 的自定义子类,支持您将 in 运算符定义为 "contains (part of) an element exactly once",如下所示:

class argList(list):
    def __contains__(self, other):
        "Check if <other> is a substring of exactly one element of <self>"
        num = 0
        for item in self:
            if other in item:
                num += 1
            if num > 1:
                return False
        return num==1
    def find(self, other):
        "Return the first element of <self> in which <other> can be found"
        for item in self:
            if other in item:
                return item

然后您可以使用它来构建您的参数列表:

>>> l = argList(["rock", "paper", "scissors"])
>>> "rock" in l
True
>>> "ck" in l
True
>>> "r" in l
False
>>> "q" in l
False

并用它来构造你的解析器:

>>> import argparse
>>> parser = argparse.ArgumentParser()
>>> l = argList(["rock", "paper", "scissors"])
>>> parser.add_argument("move", choices=l)
_StoreAction(option_strings=[], dest='move', nargs=None, const=None, default=Non
e, type=None, choices=['rock', 'paper', 'scissors'], help=None, metavar=None)

现在它可以正确处理参数(尽管错误消息仍然有点误导):

>>> parser.parse_args(["rock"])
Namespace(move='rock')
>>> parser.parse_args(["r"])
usage: [-h] {rock,paper,scissors}
: error: argument move: invalid choice: 'r' (choose from 'rock', 'paper', 'scissors')

请注意,输入的参数将被保留(当然)可能不完整:

>>> parser.parse_args(["ck"])
Namespace(move='ck')

所以你必须找出你选择了哪个实际论点:

>>> args = parser.parse_args(["ck"])
>>> l.find(vars(args)["move"])
'rock'

我还没有进一步测试这个 - 可能是重新定义 in` 在参数列表中有意想不到的副作用,我想这种程序行为是否违反了最小惊喜原则是值得怀疑的 - 但这是一个开始。

Tim 的解决方案非常有效,但如果您不想费力地继承 list,您可以利用 type 参数并定义您自己的类型函数:

choices = ['rock','paper','scissors']

def substring(s):
    options = [c for c in choices if s in c]
    if len(options) == 1:
        return options[0]
    return s

parser = argparse.ArgumentParser()
parser.add_argument("move", choices = choices, type = substring)

当您传入移动参数时,参数字符串将被传递给您的子字符串函数。然后,如果参数字符串恰好是您在 choices 列表中定义的字符串之一的子字符串,它将 return 该选择。否则,它将 return 您的原始参数字符串和 argparse 然后可能会通过 invalid choice 错误。