从命令行向Python传递列表

28

我想在命令行上提供一些参数时运行我的Python脚本。然而,其中一个参数应该是脚本的一个特定部分的选项列表。使用字符串解析是否是唯一的方法,可以在“命令行列表”字符串从逗号中拆分后构造列表?如果是,您会如何处理?

例如:-details = ['name', 'title', 'address']


2
http://docs.python.org/library/argparse.html#module-argparse - phooji
你的问题是如何在“命令行列表”字符串从逗号中分离后构建列表吗?这是我理解你的问题的方式,请澄清。 - Thomas
1
看起来他想从命令行传入一个Python列表。 - Serdalis
1
你是否曾经找到过内置于argparse命令库或类似的解决方案,而不需要潜在的hacky自定义代码? - Charlie Parker
4个回答

30

程序:

import sys, ast, getopt, types

def main(argv):            
    arg_dict={}
    switches={'li':list,'di':dict,'tu':tuple}
    singles=''.join([x[0]+':' for x in switches])
    long_form=[x+'=' for x in switches]
    d={x[0]+':':'--'+x for x in switches}
    try:            
        opts, args = getopt.getopt(argv, singles, long_form)
    except getopt.GetoptError:          
        print "bad arg"                       
        sys.exit(2)       

    for opt, arg in opts:        
        if opt[1]+':' in d: o=d[opt[1]+':'][2:]
        elif opt in d.values(): o=opt[2:]
        else: o =''
        print opt, arg,o
        if o and arg:
            arg_dict[o]=ast.literal_eval(arg)

        if not o or not isinstance(arg_dict[o], switches[o]):    
            print opt, arg, " Error: bad arg"
            sys.exit(2)                 

    for e in arg_dict:
        print e, arg_dict[e], type(arg_dict[e])        

if __name__ == '__main__':
    main(sys.argv[1:])        

命令行:

python py.py --l='[1,2,3,[1,2,3]]' -d "{1:'one',2:'two',3:'three'}" --tu='(1,2,3)'

输出:

args:  ['--l=[1,2,3,[1,2,3]]', '-d', "{1:'one',2:'two',3:'three'}", '--tu=(1,2,3)']
tu (1, 2, 3) <type 'tuple'>
di {1: 'one', 2: 'two', 3: 'three'} <type 'dict'>
li [1, 2, 3, [1, 2, 3]] <type 'list'>

此代码段可接受短或长的命令开关,例如-l--li=,并将开关后面的文本解析为Python数据结构,如列表、元组或字典。解析后的数据结构以长形式的开关键为键存储在字典中。

使用ast.literal_eval相对安全,它只能解析Python数据定义。


27

argparse 对此非常适用,它从2.7和3.2开始成为标准库的一部分,但如果没有的话,您可以通过pip install进行安装。

您主要关注的指定可变长度列表的问题可以通过在Shell中使用引号将列表解释为单个参数来解决(可能取决于您的Shell):

% python prog.py 'name title address' spam

其中prog.py包含以下内容:

import sys
my_list = sys.argv[1].split() 
# my_list is ['name', 'title', 'address']
if 'name' in my_list:
   do_something()

或类似方法。使用split参数来确定列表的分隔符:

% python prog.py "you're a foo, lift the bar"

my_list = [x.strip() for x in  sys.argv[1].split(',')]
# my_list is ["you're a foo", "lift the bar"]

但请使用argparse代替;特别是如果您想使用-c风格的标志。
解释您问题的一种方式是:
“我已经在使用argparse,因为这是在Python中解释命令行参数的明智方法。如何指定某些选项属于特定类别?”
在您的问题中,您展示了我使用的shell会抛出错误的示例;
% python prog.py -v -details=['name', 'title', 'address'] --quickly -t 4

如果使用空格分隔参数,并使用 [ 和 ] 作为 shell 语法,则无法将其解析为 Python。

我建议改用以下方法:

% python prog.py -v --details name title address --quickly -t 4

这里是关于一个名为prog.py的文件的说明:

import argparse

parser = argparse.ArgumentParser() 
parser.add_argument('-v', action='store_true')
parser.add_argument('--details', nargs='*')
parser.add_argument('--quickly', action='store_true')
parser.add_argument('-t')

args = parser.parse_args()
#args is Namespace(details=['asdf', 'a', 'a'], quickly=False, t='4', v=True)
details = args.details
#details is ['asdf', 'a', 'a']

现在,根据您的问题,您不必自己进行字符串解析。

1
是的,根据问题,提问者已经弄清了.split()部分。问题出在下一步。 - Ignacio Vazquez-Abrams
嗯,再次阅读问题,我同意,提问者缺少的是不清楚的内容。 - Thomas
你为什么要使用 x.strip() - Charlie Parker
@CharlieParker 看起来我想要去掉 " lift the bar" 中本来存在的前导空格。 - Thomas

4

是的,argparse 是你最好的选择,如果你想要为命名参数提供值列表,看起来应该是这样的(使用 nargs 参数是实现这个目标的关键):

>>> import argparse
>>> arg_parser = argparse.ArgumentParser()
>>> arg_parser.add_argument('--details',
                            nargs='*',
                            type=str,
                            default=[],
                            help='a list of the details')

# your args on the command line like this example
>>> the_args = arg_parser.parse_args("--details 'name' 'title' 'address'".split())
>>> print the_args.details
["'name'", "'title'", "'address'"])

0

我非常喜欢the-wolf使用可变长度集合作为显式字符串参数的方法。

在我看来,nargs='*'有值得注意的缺点:当尝试将字符串收集为位置参数(至少必须存在一个字符串)或者如果您正在尝试使用子解析器时,您会发现nargs='*'nargs='+'使用贪婪完成,并且它们似乎没有停止消耗任何好的理由。即使出现可选参数或数字的语法,string()类型仍将继续消耗。(这在子解析器中变得更难预测)。

最好的情况是,在(位置和可选)参数之后放置的参数被忽略了,更糟糕的是,您可能会向argparse数组传递损坏的数据类型。

我们应该能够定义一个自定义ActionType,该类型正在寻找带引号的字符串。如果找到一个,那么我们就可以适应the-wolf的例子(似乎几乎是逐字逐句的)。

这使得argparse保持清洁,并使可变集合的一般使用变得不那么麻烦。


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