解析命令行参数的最佳方法是什么?

351

在解析Python命令行参数方面,哪种方法或库是最简单简洁灵活的?

15个回答

417

argparse 是正确的选择。以下是如何使用它的简要概述:

1)初始化

import argparse

# Instantiate the parser
parser = argparse.ArgumentParser(description='Optional app description')

2) 添加参数

# Required positional argument
parser.add_argument('pos_arg', type=int,
                    help='A required integer positional argument')

# Optional positional argument
parser.add_argument('opt_pos_arg', type=int, nargs='?',
                    help='An optional integer positional argument')

# Optional argument
parser.add_argument('--opt_arg', type=int,
                    help='An optional integer argument')

# Switch
parser.add_argument('--switch', action='store_true',
                    help='A boolean switch')

3) 解析

args = parser.parse_args()

4) 访问

print("Argument values:")
print(args.pos_arg)
print(args.opt_pos_arg)
print(args.opt_arg)
print(args.switch)

5) 检查数值

if args.pos_arg > 10:
    parser.error("pos_arg cannot be larger than 10")

使用方法

正确使用:

$ ./app 1 2 --opt_arg 3 --switch

Argument values:
1
2
3
True

错误的参数:

$ ./app foo 2 --opt_arg 3 --switch
usage: convert [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
app: error: argument pos_arg: invalid int value: 'foo'

$ ./app 11 2 --opt_arg 3
Argument values:
11
2
3
False
usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]
convert: error: pos_arg cannot be larger than 10

完整帮助:

$ ./app -h

usage: app [-h] [--opt_arg OPT_ARG] [--switch] pos_arg [opt_pos_arg]

Optional app description

positional arguments:
  pos_arg            A required integer positional argument
  opt_pos_arg        An optional integer positional argument

optional arguments:
  -h, --help         show this help message and exit
  --opt_arg OPT_ARG  An optional integer argument
  --switch           A boolean switch

19
这段内容非常简洁实用,以下是官方文档链接(为了方便):https://docs.python.org/3/library/argparse.html - Christophe Roussy
1
如果您觉得argparse过于冗长,请改用plac - Nimitz14
你也可以尝试使用 https://github.com/jkulhanek/aparse/,它扩展了 argparse 或 click,并支持类型。 - jkulhanek
@nimitz14:您提供的链接无法访问。 - j.c
https://github.com/ialbert/plac - Nimitz14
@Nimitz14请将不同的软件包/解决方案作为不同的答案发布,并附上使用细节。 - not2qubit

211
这个答案建议使用optparse,适用于较早版本的Python。对于Python 2.7及以上版本,argparse替代了optparse。有关更多信息,请参见此答案

正如其他人所指出的,与getopt相比,你最好选择optparse。 getopt基本上是标准getopt(3)C库函数的一对一映射,并且不太容易使用。

虽然optparse略微冗长,但结构更好,以后扩展也更简单。

以下是向解析器添加选项的典型行:

parser.add_option('-q', '--query',
            action="store", dest="query",
            help="query string", default="spam")

它几乎是自解释的,处理时,它将接受-q或--query作为选项,将参数存储在称为query的属性中,并且如果您没有指定它,则具有默认值。此外,它也是自我记录的,因为您在选项旁边声明了帮助参数(当使用-h / --help运行时将使用该参数)。

通常您会使用以下方式解析参数:

options, args = parser.parse_args()

默认情况下,这将解析传递给脚本的标准参数(sys.argv[1:])。

options.query 将被设置为您传递给脚本的值。

只需执行以下操作即可创建解析器

parser = optparse.OptionParser()

以下是您所需的所有基础知识。下面是一个完整的 Python 脚本,显示了这一点:

import optparse

parser = optparse.OptionParser()

parser.add_option('-q', '--query',
    action="store", dest="query",
    help="query string", default="spam")

options, args = parser.parse_args()

print 'Query string:', options.query

展示Python基础知识的5行代码。

将其保存在sample.py文件中,使用以下命令运行一次:

python sample.py

并且一次使用

python sample.py --query myquery

除此之外,您会发现 optparse 很容易扩展。在我的一个项目中,我创建了一个 Command 类,它可以轻松地将子命令嵌套在命令树中。它大量使用 optparse 来链接命令。这不是我可以在几行说明清楚的东西,但请随意在我的存储库中浏览主要类,以及使用它和选项解析器的类。您可以点击以下链接:主要类使用它和选项解析器的类

11
这个答案非常清晰易懂,适用于Python 2.3到2.6版本。但对于Python 2.7及以上版本,这不是最佳答案,因为argparse现在已成为标准库的一部分,而optparse已被弃用。 - matt wilkie
在我的情况下,我想对我的应用程序进行性能分析以检测缓慢问题。还有另一个名为[tuna](https://github.com/nschloe/tuna)的工具,它允许我通过添加参数`-mcProfile -o program.prof`来分析整个应用程序,但是argparcer正在捕获这些参数,我该如何将这些参数传递给Python可执行文件? - Yogeshwar Singh

95

使用docopt

自2012年以来,有一个非常简单、强大和真正“酷”的参数解析模块叫做docopt。以下示例摘自其文档:

"""Naval Fate.

Usage:
  naval_fate.py ship new <name>...
  naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
  naval_fate.py ship shoot <x> <y>
  naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

"""
from docopt import docopt


if __name__ == '__main__':
    arguments = docopt(__doc__, version='Naval Fate 2.0')
    print(arguments)

就是这样:只需两行代码再加上必不可少的文档字符串,您就可以解析参数并在参数对象中使用。

使用python-fire

自2017年以来,还有另一个很酷的模块称为python-fire。它可以为您的代码生成一个CLI界面,而您无需进行任何参数解析。这里是文档中的一个简单示例(此小程序将函数double公开到命令行):

import fire

class Calculator(object):

  def double(self, number):
    return 2 * number

if __name__ == '__main__':
  fire.Fire(Calculator)

你可以从命令行运行以下命令:

> calculator.py double 10
20
> calculator.py double --number=15
30

4
docopt为Python模块,因此需要安装后才能使用。也许是因为docopt被作为一个标准库或已经被其他依赖项安装过了,所以它看起来不需要单独安装。但如果遇到"ImportError: No module named docopt"的错误,就说明需要单独安装docopt模块。 - keen
2
@keen,它肯定不是 Python 的一部分,但你不需要安装它:"你可以将 docopt.py 文件直接拖放到你的项目中——它是自包含的"——https://github.com/docopt/docopt - ndemou
15
我们只是对“安装”的定义有所不同 - 我想为未来的读者指出这一点。 - keen
1
@keen 我已经添加了一个关于“无需安装”的注释,以便与您共享定义的人可以知道。 :-) - ndemou

41

我更喜欢 Click。它抽象了管理选项的过程,并允许“用尽可能少的代码以可组合的方式创建美丽的命令行界面”。

以下是使用示例:

import click

@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
              help='The person to greet.')
def hello(count, name):
    """Simple program that greets NAME for a total of COUNT times."""
    for x in range(count):
        click.echo('Hello %s!' % name)

if __name__ == '__main__':
    hello()

它还会自动生成漂亮格式的帮助页面:

$ python hello.py --help
Usage: hello.py [OPTIONS]

  Simple program that greets NAME for a total of COUNT times.

Options:
  --count INTEGER  Number of greetings.
  --name TEXT      The person to greet.
  --help           Show this message and exit.

41

使用 argparse 的原因是它是一种新的时髦方式,这些 原因使其优于 optparse 和 getopt。

更新:自 py2.7 开始,argparse 已成为标准库的一部分,而 optparse 已被弃用。


你的主链接是404,所以我用一个指向一个讨论同一主题的SO问题的链接来替换它。 - Joe Holloway

15

几乎每个人都在使用getopt

以下是文档的示例代码:

import getopt, sys

def main():
    try:
        opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output="])
    except getopt.GetoptError:
        # print help information and exit:
        usage()
        sys.exit(2)
    output = None
    verbose = False
    for o, a in opts:
        if o == "-v":
            verbose = True
        if o in ("-h", "--help"):
            usage()
            sys.exit()
        if o in ("-o", "--output"):
            output = a

总之,这就是它的工作原理。

你有两种类型的选项。那些接收参数的和那些就像开关一样。

sys.argv 在很大程度上类似于 C 中的 char** argv。就像在 C 中一样,跳过第一个元素,即程序的名称,并仅解析参数:sys.argv[1:]

Getopt.getopt 将根据您在参数中给出的规则进行解析。

"ho:v" 这里描述了短参数:-ONELETTER: 表示 -o 接受一个参数。

最后,["help", "output="] 描述了长参数(--MORETHANONELETTER)。 输出后面的 = 再次表示输出接受一个参数。

结果是一个选项和参数的列表

如果一个选项不接受任何参数(就像这里的 --help),则 arg 部分为空字符串。 然后通常希望在此列表上循环并测试选项名称,就像示例中一样。

我希望这能帮到你。


9
随着Python新版本中getopt的弃用,这个答案已经过时了。 - shuttle87
1
@shuttle87 截至Python3.7.2版本,getopt仍未被弃用... 但是它的文档指出,它主要是为熟悉C语言getopt()函数的用户提供的,并承认对于其他用户来说,使用argparse可能是更好的解决方案,可以"编写更少的代码并获得更好的帮助和错误消息"。 - Skippy le Grand Gourou

14

使用附带于标准库的optparse。例如:

#!/usr/bin/env python
import optparse

def main():
  p = optparse.OptionParser()
  p.add_option('--person', '-p', default="world")
  options, arguments = p.parse_args()
  print 'Hello %s' % options.person

if __name__ == '__main__':
  main()

来源: 使用Python创建UNIX命令行工具

然而,自Python 2.7起optparse已被弃用,请参见:为什么要使用argparse而不是optparse?


14

轻量级命令行参数默认值

虽然 argparse 很棒,对于完全记录的命令行开关和高级功能也是正确答案,但您可以使用函数参数默认值来处理非常简单的位置参数。

import sys

def get_args(name='default', first='a', second=2):
    return first, int(second)

first, second = get_args(*sys.argv)
print first, second

'name'参数用于捕获脚本名称,但不会被使用。测试输出的样式如下:

> ./test.py
a 2
> ./test.py A
A 2
> ./test.py A 20
A 20

对于我只需要一些默认值的简单脚本,我发现这已经足够了。您可能还希望在返回值或命令行值中包括一些类型转换,以确保它们不全是字符串。


7

如果您需要在Win32(2K、XP等)上获取Unicode参数,以下内容可能会有所帮助:


from ctypes import *

def wmain(argc, argv):
    print argc
    for i in argv:
        print i
    return 0

def startup():
    size = c_int()
    ptr = windll.shell32.CommandLineToArgvW(windll.kernel32.GetCommandLineW(), byref(size))
    ref = c_wchar_p * size.value
    raw = ref.from_address(ptr)
    args = [arg for arg in raw]
    windll.kernel32.LocalFree(ptr)
    exit(wmain(len(args), args))
startup()

谢谢。这个脚本帮助我解决了一些非常复杂的引用问题,当我需要传递启动命令给GVim时。 - telotortium

6

Argparse的代码可能比实际实现代码还要长!

我发现大多数流行的参数解析选项存在这样一个问题:如果您的参数只是适度的话,为了记录它们,所编写的代码就会与提供的好处不成比例。

在参数解析场景中,一个相对较新的选择(我认为)是plac

它与argparse有一些公认的权衡,但使用内联文档并简单地包装main()类型函数:

def main(excel_file_path: "Path to input training file.",
     excel_sheet_name:"Name of the excel sheet containing training data including columns 'Label' and 'Description'.",
     existing_model_path: "Path to an existing model to refine."=None,
     batch_size_start: "The smallest size of any minibatch."=10.,
     batch_size_stop:  "The largest size of any minibatch."=250.,
     batch_size_step:  "The step for increase in minibatch size."=1.002,
     batch_test_steps: "Flag.  If True, show minibatch steps."=False):
"Train a Spacy (http://spacy.io/) text classification model with gold document and label data until the model nears convergence (LOSS < 0.5)."

    pass # Implementation code goes here!

if __name__ == '__main__':
    import plac; plac.call(main)

1
信息提示:plac 的最佳用法(如示例所示)仅适用于 Python 3.x,因为它使用 3.x 函数注释。 - DisappointedByUnaccountableMod

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