每个位置参数的可选参数
我想用 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')
不是我想要的,因为这样会让每个可选参数失去它所关联的位置参数。
3 个回答
使用 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 则可以获取位置参数。
你可以有位置参数来接收值,或者设置为真或假等等,如果我没记错的话。
你可以自己一步一步查看 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
这里有一个简单的小技巧,我觉得可以作为一个不错的起点:
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
是同一个列表)。