使用Python的optparse模块,如何创建一个可以接受可变数量参数的选项?

12

使用Perl的Getopt::Long,您可以轻松定义接受可变数量参数的命令行选项:

foo.pl --files a.txt             --verbose
foo.pl --files a.txt b.txt c.txt --verbose

有没有办法直接使用Python的optparse模块来实现这个功能?据我所知,nargs选项属性可用于指定一定数量的选项参数,并且在文档中我没有看到其他替代方案。


1
通过参数指定文件名,而非选项:foo.pl a.txt b.txt c.txt --verbose。在这种情况下,文件名将被放置在参数中。 - jfs
如果--files定义了输入,则不建议使用此方法。 - S.Lott
6个回答

21

这花了我一点时间才弄清楚,但你可以使用回调操作来将选项设置为完成此操作。请查看我在此示例中如何获取“--file”标志的任意数量参数。

from optparse import OptionParser,

def cb(option, opt_str, value, parser):
        args=[]
        for arg in parser.rargs:
                if arg[0] != "-":
                        args.append(arg)
                else:
                        del parser.rargs[:len(args)]
                        break
        if getattr(parser.values, option.dest):
                args.extend(getattr(parser.values, option.dest))
        setattr(parser.values, option.dest, args)

parser=OptionParser()
parser.add_option("-q", "--quiet",
        action="store_false", dest="verbose",
        help="be vewwy quiet (I'm hunting wabbits)")
parser.add_option("-f", "--filename",
        action="callback", callback=cb, dest="file")

(options, args) = parser.parse_args()

print options.file
print args

唯一的副作用是你获得一个参数列表而不是元组。但这可以很容易地解决,对于我的特定用例,列表是理想的。


我更倾向于FMc答案中所示的Python文档示例:https://dev59.com/uHNA5IYBdhLWcg3wSrqa#1025230 - Stefano
2
两者在功能上是等效的,除了示例代码还允许像“-1”这样的值被解析为标志的参数,这对于某些用例来说可能是一个不错的特性。 - Dave Rawks
确实,它允许负数(我也喜欢它直接来自官方文档 :) ) - Stefano

10

4
这绝对是确切问题的最佳答案;argparse更好,但你并不总是能够轻松地使用它:例如在Django管理命令中。 - Stefano

8
我相信optparse不支持您需要的功能(直接来说是不支持-正如您所注意到的,如果您愿意做所有额外的工作,可以使用回调函数来实现!)。您也可以使用第三方扩展argparse来最简单地完成此操作,它支持可变数量的参数(还添加了几个其他有用的功能)。 这个URL记录了argparseadd_argument -- 通过传递nargs =' *',选项可以采取零个或多个参数,'+'让它采取一个或多个参数等。

2
没错,但如果你还不能升级到2.7(前天发布),第三方包仍然是救星!-) - Alex Martelli

2
你使用这个会更好吗?
foo.pl --files a.txt,b.txt,c.txt --verbose

1
不行,绝对不可以。例如,这将禁止像 foo.py *.txt 这样的 shell 扩展。 - Constantinius
我认为它不应该被评为-1,在某些情况下,它可能是有效的解决方案,比如文档链接中的浮点数列表。也许质疑的语气有点不对(但我又怎么知道呢...)。 - dhill
@Constantinius - Shell扩展并不总是理想的。如果你正在处理大量的输入文件,你可能会遇到任意的命令行长度限制(由于它根据你的环境大小而缩小,因此是动态的),在这种情况下,最好使用perl glob的bsd_glob方法,在接收参数后进行扩展。我通常在引号内提供多个文件/ glob模式,以空格分隔。 - hepcat72

1
我最近也遇到了这个问题:我正在使用Python 2.6,并需要一个选项来获取可变数量的参数。我尝试使用Dave的解决方案,但发现如果没有显式地将nargs设置为0,则无法正常工作。
def arg_list(option, opt_str, value, parser):
    args = set()
    for arg in parser.rargs:
        if arg[0] == '-':
            break
        args.add(arg)
        parser.rargs.pop(0)
    setattr(parser.values, option.dest, args)

parser=OptionParser()
parser.disable_interspersed_args()
parser.add_option("-f", "--filename", action="callback", callback=arg_list,
                  dest="file", nargs=0)

(options, args) = parser.parse_args()

问题在于,默认情况下,通过add_options添加的新选项被假定为nargs = 1,并且当nargs > 0时,OptionParser将从rargs中弹出项目并将它们分配给value,然后再调用任何回调函数。因此,对于未指定nargs的选项,rargs在调用回调函数时总是少一个。
这个回调函数可以用于任意数量的选项,只需让callback_args成为要调用的函数,而不是setattr。

这个对我来说可以工作,只需要做一个小改变:"如果arg[0] == '-':" - Maoz Zadok
新手报道...(在学习遗留系统时卡住了)由于.pop的存在,这会返回每个第二个选项。def arg_list(option, opt_str, value, parser): args = set() rargs = parser.rargs[:] for arg in rargs: etc... 我试图格式化它! - temporary1

0
这是一种方法:将生成文件列表的字符串作为字符串输入,然后使用http://docs.python.org/2/library/glob.html进行扩展,尽管这可能需要转义*。
实际上,有更好的方法: python myprog.py -V -l 1000 /home/dominic/radar/*.json <- 如果这是您的命令行 parser,opts = parse_args()
inFileLst = parser.largs

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