每个位置参数的可选参数

5
使用argparse(还是其他什么?),我希望每个位置参数都有一个默认值的可选参数。

参数应该如下所示:

script.py arg1 arg2 -o 1 arg3 -o 2 arg4 arg5

我希望它能够将其解析成可用的内容,如位置参数列表和带有默认值的可选参数列表。例如,如果上面示例中可选参数的默认值为0:

positional = [arg1, arg2, arg3, arg4, arg5]
optional = [0, 1, 2, 0, 0]

换句话说,parser.add_argument('-o', action='append') 不是我想要的,因为我会失去每个可选参数所关联的位置参数。

你知道你会有多少个位置参数吗? - mgilson
我想循环遍历参数,不管有多少个。 - bkanuka
3个回答

3

这是一个我整理的简单技巧,可能是一个不错的起点:

import argparse

class PositionalAction(argparse.Action):
    def __call__(self,parser,namespace,values,option_string=None):
        lst = getattr(namespace,self.dest)
        lst.append(values)
        parser.last_positional_values = lst
        all_positional = getattr(namespace,'all_positional',[])
        all_positional.append(lst)
        namespace.all_positional = all_positional

class AssociateAction(argparse.Action):
    def __call__(self,parser,namespace,values,option_string=None):
        try:
            parser.last_positional_values.append(values)
        except AttributeError:
            pass

parser = argparse.ArgumentParser()
parser.add_argument('-o',action=AssociateAction,dest=argparse.SUPPRESS)
junk,unknown = parser.parse_known_args()

for i,_ in enumerate(unknown):
    parser.add_argument('arg%d'%i,action=PositionalAction,default=[])

print parser.parse_args()

这是它的实际运用:

temp $ python test1.py foo -o 1 bar -o 2 baz qux -o 4
Namespace(all_positional=[['foo', '1'], ['bar', '2'], ['baz'], ['qux', '4']], arg0=['foo', '1'], arg1=['bar', '2'], arg2=['baz'], arg3=['qux', '4'])

这个问题有几个挑战。首先,你想接受任意数量的位置参数 - argparse 不喜欢那样做。argparse 想要事先知道要期望什么。解决方案是构建一个解析器并解析命令行,但告诉 argparse 仅解析已知参数(在这种情况下,非位置 -o 参数都被静默解析,但“位置”参数没有被解析)。parse_known_args 对此非常完美,因为它返回一个元组,格式为 (已解析内容的名称空间,未知参数)。现在我们知道了未知的参数 - 我们只需要为每个参数向解析器添加一个位置参数,以使 parse_args 正常运行。
那么,自定义操作实际上是在做什么呢?当发现一个位置参数(第二次遍历时),我们获得默认值(即列表)并将该值添加到该列表中(此后我将其称为“值”列表)。然后,我们通过引用“值”列表来修改解析器。我们还从名称空间获取“all_positional”列表。如果它没有该属性,我们只会得到一个空列表。我们将“值”列表添加到“all_positional”列表中,并将其放回名称空间中。
现在,当我们遇到一个 -o 标志时,我们查看解析器以获取“值”列表,并将附加值添加到该列表中。我们可以完全不接触解析器做同样的事情...(我们可以查看 namespace.all_positional[-1] - 它是与 parser.last_positional_values 相同的列表)。

你所创造的这个神奇东西似乎做到了我想要的(或者足够接近),但是我需要一段时间来完全理解你所做的事情。不管怎样,谢谢! - bkanuka
我会添加一些注释,希望能够更易于理解。 - mgilson
不用担心,这些误解肯定更多地与我的专业知识不足有关,而不是缺乏清晰度。 - bkanuka

0

你可以尝试自己遍历sys.argv参数。看起来你并不需要argparse提供的额外功能。例如:

argv='script.py arg1 arg2 -o 1 arg3 -o 2 arg4 arg5'.split()
pos=[]
opt=[]
i=1
while i<len(argv):
    a=argv[i]
    if a[:2]!='-o':
        pos.append(a)
        opt.append(0)
    else:
        i += 1
        opt[-1]=argv[i]
    i += 1

-1

使用optparse,它非常强大,尽管有点复杂:

op = optparse.OptionParser(usage=usage)

op.add_option('-c','--cmd',dest='cmd',help="""Command to run. Mutually exclusive with -s. You can use string ${ADDR} to have it replaced with specified host address in the command. """)

op.add_option('-s','--script',dest='script',help="""Script file to run remotely. Mutually exclusive with -c. A script can have its own arguments; specify the whole command in doublequotes, like "script -arg arg".""")

op.add_option('-l','--replicate-owner',dest='replicateowner',action="store_true",help="""Replicate (symbolic) owner and (symbolic) group of the file on the remote host, if possible. If remote account with username the same as local account does not exist on remote host, this will silently fail.""")

# parse cmdline options
(opts, args) = op.parse_args()

opts 可以获取命名参数,args 可以获取位置参数。

你可以使用位置参数来接收值或设置为 true 或 false,还有更多的选项(如果我没记错的话)。


2
这并没有解答特定的问题。发帖人似乎已经了解如何一般使用 {opt,arg}parse。 - chepner

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接