从命令行向Python传递列表

28 投票
4 回答
58420 浏览
提问于 2025-04-17 03:24

我想让我的Python脚本能够通过命令行运行,并且可以接收一些参数。不过,其中一个参数应该是特定于脚本某个部分的选项列表。请问,唯一的方法是通过字符串解析来实现吗?也就是说,在把命令行输入的字符串用逗号分开后,再构建这个列表?如果是这样的话,你会怎么做呢?

举个例子:-details=['name', 'title', 'address']

4 个回答

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'"])
27

argparse 是一个很不错的工具,它在 Python 2.7 和 3.2 版本的标准库中都有,但如果你用的版本不支持,也可以通过 pip install 来安装。

你主要担心的是如何指定一个可变长度的列表,这个问题可以通过在命令行中用引号将列表作为一个整体来解决(具体可能还要看你用的命令行工具):

% 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 中命令行参数的合理方式。我该如何指定某些选项属于特定类别?”

在你的问题中,你给出了一个例子,这在我用的命令行工具中会出错;

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

因为它们会用空格来分隔参数,可能还会把 [ 和 ] 当作命令行语法来处理,这样就无法传递给 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']

现在,针对你的问题,你不需要自己去解析字符串了。

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的数据定义。

撰写回答