从命令行将列表传递给Python

时间:2011-09-30 03:22:02

标签: python command-line command-line-arguments command-line-parsing

我想在提供一些参数的时候从命令行运行我的python脚本。但是,其中一个参数应该是特定于脚本的一个段的选项列表。在从逗号分割“命令行列表”字符串后,通过实际构建列表,字符串解析是唯一的方法吗?如果是这样,你会怎么做?

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

4 个答案:

答案 0 :(得分:28)

<强>程序:

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=之类的短或长命令切换,并在切换到像list,tuple或dict之类的Python数据结构后解析文本。解析的数据结构最终在具有长格式切换键的字典中。

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

答案 1 :(得分:26)

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()

或类似的。使用带分割的参数来分隔列表:

% 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中命令行参数的明智方法。如何指定某些选项在特定类别中?”

在你的问题中,你已经展示了我使用的外壳会阻塞的一些例子;

% python prog.py -v -details=['name', 'title', 'address'] --quickly -t 4

不会让python被解析,因为他们使用空格来分隔参数,并且可能使用[和]作为shell语法。

我建议以下代替

% 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']

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

答案 2 :(得分:3)

是的,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'"])

答案 3 :(得分:0)

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

在我看来,nargs='*'有一些值得注意的缺点:当尝试将字符串作为位置参数(至少必须存在一个字符串)时,或者如果您尝试使用子分析符,您会发现{{1}并且nargs='*'使用贪婪的完成,并且他们似乎没有因为任何好的理由而停止消费。即使可选参数或数字的语法出现,string()类型也会继续消耗。 (这对子分析师的预期变得越来越难。)

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

我们应该能够定义一个正在寻找带引号的字符串的自定义ActionType。如果找到一个,那么我们会调整the-wolf的例子(几乎逐字逐句)。

这样可以在argparse中保持干净,并且可以更好地使用变量集合。