optparse — 命令行选项解析器

源代码: Lib/optparse.py


选择参数解析库

标准库包含三个参数解析库

  • getopt: 一个紧密模仿 C 语言过程式 getopt API 的模块。自 Python 1.0 初始版本发布前就已包含在标准库中。

  • optparsegetopt 的一个声明式替代品,提供等效功能,而无需每个应用程序实现自己的过程式选项解析逻辑。自 Python 2.3 版本起包含在标准库中。

  • argparse: optparse 的一个更具主见(opinionated)的替代方案,默认提供更多功能,但代价是应用程序在精确控制参数处理方式方面的灵活性降低。自 Python 2.7 和 Python 3.2 版本起包含在标准库中。

在没有更具体的参数解析设计约束的情况下,推荐使用 argparse 来实现命令行应用程序,因为它能以最少的应用程序级代码提供最高水平的基线功能。

getopt 的保留几乎完全是出于向后兼容性的原因。然而,它也服务于一个特定的用例,即作为在基于 getopt 的 C 应用程序中原型化和测试命令行参数处理的工具。

在以下情况下,应考虑将 optparse 作为 argparse 的替代方案

  • 一个应用程序已经在使用 optparse,并且不想冒险因迁移到 argparse 而可能引起的细微行为变化

  • 应用程序需要对选项和位置参数在命令行上的交错方式有额外的控制(包括完全禁用交错功能的能力)

  • 应用程序需要对命令行元素的增量解析有额外的控制(虽然 argparse 支持这一点,但其在实践中的具体工作方式对于某些用例来说并不理想)

  • 应用程序需要对处理接受可能以 - 开头的参数值的选项有额外的控制(例如,要传递给被调用子进程的委托选项)

  • 应用程序需要一些 argparse 不支持,但可以通过 optparse 提供的更底层接口实现的其他命令行参数处理行为

这些考虑也意味着,对于编写第三方命令行参数处理库的库作者来说,optparse 可能会提供一个更好的基础。

举一个具体的例子,考虑以下两种命令行参数解析配置,第一种使用 optparse,第二种使用 argparse

import optparse

if __name__ == '__main__':
    parser = optparse.OptionParser()
    parser.add_option('-o', '--output')
    parser.add_option('-v', dest='verbose', action='store_true')
    opts, args = parser.parse_args()
    process(args, output=opts.output, verbose=opts.verbose)
import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-o', '--output')
    parser.add_argument('-v', dest='verbose', action='store_true')
    parser.add_argument('rest', nargs='*')
    args = parser.parse_args()
    process(args.rest, output=args.output, verbose=args.verbose)

最明显的区别是,在 optparse 版本中,非选项参数是在选项处理完成后由应用程序单独处理的。而在 argparse 版本中,位置参数的声明和处理方式与命名选项相同。

然而,argparse 版本处理某些参数组合的方式也与 optparse 版本不同。例如(以及其他差异):

  • 提供 -o -v 时,使用 optparse 会得到 output="-v"verbose=False,但使用 argparse 会产生用法错误(抱怨没有为 -o/--output 提供值,因为 -v 被解释为详细模式标志)。

  • 类似地,提供 -o -- 时,使用 optparse 会得到 output="--"args=(),但使用 argparse 会产生用法错误(同样抱怨没有为 -o/--output 提供值,因为 -- 被解释为终止选项处理,并将所有剩余的值视为位置参数)。

  • 提供 -o=foo 时,使用 optparse 会得到 output="=foo",但使用 argparse 会得到 output="foo"(因为 = 被特殊处理为选项参数值的替代分隔符)。

argparse 版本中的这些不同行为是被认为是可取的还是一个问题,将取决于具体的命令行应用程序用例。

参见

click 是一个第三方参数处理库(最初基于 optparse),它允许将命令行应用程序开发为一组经过装饰的命令实现函数。

其他第三方库,如 typermsgspec-click,允许以更有效地与 Python 类型注解的静态检查集成的方式指定命令行接口。

引言

与极简的 getopt 模块相比,optparse 是一个更方便、灵活且功能强大的命令行选项解析库。optparse 使用更具声明性的命令行解析风格:你创建一个 OptionParser 的实例,用选项填充它,然后解析命令行。optparse 允许用户以传统的 GNU/POSIX 语法指定选项,并且还会为你生成用法和帮助信息。

下面是在一个简单脚本中使用 optparse 的示例

from optparse import OptionParser
...
parser = OptionParser()
parser.add_option("-f", "--file", dest="filename",
                  help="write report to FILE", metavar="FILE")
parser.add_option("-q", "--quiet",
                  action="store_false", dest="verbose", default=True,
                  help="don't print status messages to stdout")

(options, args) = parser.parse_args()

通过这几行代码,你的脚本用户现在可以在命令行上执行“常规操作”,例如:

<yourscript> --file=outfile -q

在解析命令行时,optparse 会根据用户提供的命令行值来设置 parse_args() 返回的 options 对象的属性。当 parse_args() 从解析此命令行返回时,options.filename 将是 "outfile",而 options.verbose 将是 Falseoptparse 支持长选项和短选项,允许将短选项合并在一起,并允许选项以多种方式与其参数关联。因此,以下命令行都等同于上面的示例:

<yourscript> -f outfile --quiet
<yourscript> --quiet --file outfile
<yourscript> -q -foutfile
<yourscript> -qfoutfile

此外,用户可以运行以下任一命令:

<yourscript> -h
<yourscript> --help

optparse 将会打印出你脚本选项的简要摘要:

Usage: <yourscript> [options]

Options:
  -h, --help            show this help message and exit
  -f FILE, --file=FILE  write report to FILE
  -q, --quiet           don't print status messages to stdout

其中 *yourscript* 的值是在运行时确定的(通常来自 sys.argv[0])。

背景

optparse 的设计明确旨在鼓励创建具有直观命令行界面的程序,这些界面遵循 C 开发者可用的 getopt() 函数族所建立的约定。为此,它只支持 Unix 下常规使用的最常见的命令行语法和语义。如果你不熟悉这些约定,阅读本节将有助于你了解它们。

术语

实参

在命令行上输入并通过 shell 传递给 execl()execv() 的字符串。在 Python 中,参数是 sys.argv[1:] 的元素(sys.argv[0] 是正在执行的程序名)。Unix shell 也使用术语“word”。

有时需要替换 sys.argv[1:] 之外的参数列表,所以你应该将“参数”理解为“sys.argv[1:] 的一个元素,或者作为 sys.argv[1:] 替代品的某个其他列表的元素”。

选项

用于提供额外信息以指导或定制程序执行的参数。选项有许多不同的语法;传统的 Unix 语法是一个连字符(“-”)后跟一个单字母,例如 -x-F。此外,传统的 Unix 语法允许多个选项合并成一个参数,例如 -x -F 等同于 -xF。GNU 项目引入了 -- 后跟一系列由连字符分隔的单词,例如 --file--dry-run。这是 optparse 提供的仅有的两种选项语法。

世界上出现过的其他一些选项语法包括:

  • 一个连字符后跟几个字母,例如 -pf(这与将多个选项合并成一个参数不同

  • 一个连字符后跟整个单词,例如 -file(这在技术上等同于前一种语法,但它们通常不会在同一个程序中出现)

  • 一个加号后跟单个字母、几个字母或一个单词,例如 +f+rgb

  • 一个斜杠后跟一个字母、几个字母或一个单词,例如 /f/file

这些选项语法不受 optparse 支持,并且永远也不会支持。这是故意的:前三种在任何环境下都是非标准的,而最后一种只有在你专门针对 Windows 或某些传统平台(如 VMS、MS-DOS)时才有意义。

选项参数

跟在一个选项后面的参数,与该选项紧密相关,并且在该选项被处理时从参数列表中消耗掉。使用 optparse 时,选项参数可以与其选项在不同的参数中:

-f foo
--file foo

或包含在同一个参数中:

-ffoo
--file=foo

通常,一个给定的选项要么带参数,要么不带。很多人想要一个“可选的选项参数”功能,即某些选项如果看到参数就接受它,如果没看到就不接受。这有点争议,因为它会使解析变得模棱两可:如果 -a 接受一个可选参数,而 -b 是另一个完全不同的选项,我们该如何解释 -ab?由于这种模糊性,optparse 不支持此功能。

positional argument (位置参数)

在选项被解析后,即选项及其参数被解析并从参数列表中移除后,参数列表中剩下的东西。

必需选项

必须在命令行上提供的选项;请注意,“必需选项”这个短语在英语中是自相矛盾的。optparse 不会阻止你实现必需选项,但也不会在这方面提供太多帮助。

例如,考虑这个假设的命令行:

prog -v --report report.txt foo bar

-v--report 都是选项。假设 --report 接受一个参数,那么 report.txt 就是一个选项参数。foobar 是位置参数。

选项的用途是什么?

选项用于提供额外信息以调整或定制程序的执行。以防不清楚,选项通常是*可选的*。一个程序应该能够在没有任何选项的情况下正常运行。(从 Unix 或 GNU 工具集中随机挑选一个程序。它能在没有任何选项的情况下运行并仍然有意义吗?主要的例外是 findtardd——所有这些都是因其非标准语法和令人困惑的界面而受到合理批评的奇特异类。)

很多人希望他们的程序有“必需的选项”。想一想。如果它是必需的,那它就*不是可选的*!如果你的程序为了成功运行绝对需要某条信息,那正是位置参数的用途。

作为良好命令行界面设计的一个例子,考虑一下不起眼的 cp 工具,用于复制文件。如果不提供目标和至少一个源,尝试复制文件就没有什么意义。因此,如果你在没有参数的情况下运行 cp,它会失败。然而,它有一个灵活、有用的语法,完全不需要任何选项:

cp SOURCE DEST
cp SOURCE ... DEST-DIR

仅凭这一点你就可以走得很远。大多数 cp 的实现都提供了一堆选项来精确调整文件的复制方式:你可以保留模式和修改时间、避免跟随符号链接、在覆盖现有文件前询问等等。但所有这些都不会分散 cp 的核心任务,即将一个文件复制到另一个文件,或将多个文件复制到另一个目录。

位置参数的用途是什么?

位置参数用于那些你的程序绝对、肯定需要运行的信息片段。

一个好的用户界面应该有尽可能少的绝对要求。如果你的程序需要17个不同的信息才能成功运行,那么你*如何*从用户那里获取这些信息并不重要——大多数人会在成功运行程序之前就放弃了。这适用于命令行、配置文件或GUI:如果你对用户提出这么多要求,大多数人只会放弃。

简而言之,尽量减少用户绝对需要提供的信息量——尽可能使用合理的默认值。当然,你也希望你的程序相当灵活。这就是选项的用途。同样,无论是配置文件中的条目、GUI“首选项”对话框中的小部件,还是命令行选项,你实现的选项越多,你的程序就越灵活,其实现也就越复杂。当然,太多的灵活性也有缺点;太多的选项会让用户不知所措,并使你的代码更难维护。

教程

虽然 optparse 非常灵活和强大,但在大多数情况下使用起来也很直接。本节涵盖了任何基于 optparse 的程序中常见的代码模式。

首先,你需要导入 OptionParser 类;然后,在主程序的早期,创建一个 OptionParser 实例:

from optparse import OptionParser
...
parser = OptionParser()

然后你就可以开始定义选项了。基本语法是:

parser.add_option(opt_str, ...,
                  attr=value, ...)

每个选项都有一个或多个选项字符串,如 -f--file,以及几个选项属性,这些属性告诉 optparse 当它在命令行上遇到该选项时应该期待什么以及做什么。

通常,每个选项会有一个短选项字符串和一个长选项字符串,例如:

parser.add_option("-f", "--file", ...)

你可以自由定义任意数量的短选项字符串和长选项字符串(包括零个),只要总体上至少有一个选项字符串即可。

传递给 OptionParser.add_option() 的选项字符串实际上是该调用所定义选项的标签。为简洁起见,我们经常会提到在命令行上*遇到一个选项*;实际上,optparse 遇到的是*选项字符串*,并根据它们查找选项。

一旦你所有的选项都定义好了,就指示 optparse 解析你程序的命令行:

(options, args) = parser.parse_args()

(如果你愿意,可以向 parse_args() 传递一个自定义的参数列表,但这很少有必要:默认情况下它使用 sys.argv[1:]。)

parse_args() 返回两个值:

  • options,一个包含所有选项值的对象——例如,如果 --file 接受一个字符串参数,那么 options.file 将是用户提供的文件名,如果用户没有提供该选项,则为 None

  • args,解析选项后剩下的位置参数列表。

本教程部分仅涵盖四个最重要的选项属性:actiontypedest(目标)和 help。其中,action 是最基本的。

理解选项 action

Action 告诉 optparse 在命令行上遇到一个选项时该做什么。在 optparse 中有一组固定的、硬编码的 action;添加新的 action 是一个高级主题,将在 扩展 optparse 部分中介绍。大多数 action 告诉 optparse 将一个值存储在某个变量中——例如,从命令行获取一个字符串并将其存储在 options 的一个属性中。

如果你不指定一个选项 action,optparse 默认使用 store

store action

最常见的选项 action 是 store,它告诉 optparse 取下一个参数(或当前参数的剩余部分),确保它是正确的类型,并将其存储到你选择的目标位置。

例如:

parser.add_option("-f", "--file",
                  action="store", type="string", dest="filename")

现在让我们编造一个假的命令行,并让 optparse 来解析它:

args = ["-f", "foo.txt"]
(options, args) = parser.parse_args(args)

optparse 看到选项字符串 -f 时,它会消费下一个参数 foo.txt,并将其存储在 options.filename 中。所以,在这次调用 parse_args() 之后,options.filename 的值是 "foo.txt"

optparse 支持的其他一些选项类型是 intfloat。这里有一个期望整数参数的选项:

parser.add_option("-n", type="int", dest="num")

请注意,这个选项没有长选项字符串,这是完全可以接受的。另外,没有显式的 action,因为默认值是 store

让我们解析另一个假的命令行。这次,我们将选项参数紧挨着选项:因为 -n42(一个参数)等同于 -n 42(两个参数),所以代码

(options, args) = parser.parse_args(["-n42"])
print(options.num)

将打印 42

如果你不指定类型,optparse 会假定为 string。再加上默认的 action 是 store,这意味着我们的第一个例子可以写得更短:

parser.add_option("-f", "--file", dest="filename")

如果你不提供目标(destination),optparse 会从选项字符串中推断出一个合理的默认值:如果第一个长选项字符串是 --foo-bar,那么默认的目标就是 foo_bar。如果没有长选项字符串,optparse 会看第一个短选项字符串:-f 的默认目标是 f

optparse 还包括内置的 complex 类型。添加类型将在 扩展 optparse 部分中介绍。

处理布尔(标志)选项

标志选项——当看到特定选项时将变量设置为 true 或 false——非常常见。optparse 通过两个独立的 action,store_truestore_false,来支持它们。例如,你可能有一个 verbose 标志,用 -v 开启,用 -q 关闭:

parser.add_option("-v", action="store_true", dest="verbose")
parser.add_option("-q", action="store_false", dest="verbose")

这里我们有两个不同的选项具有相同的目标(destination),这是完全可以的。(这只意味着你在设置默认值时需要小心一点——见下文。)

optparse 在命令行上遇到 -v 时,它会将 options.verbose 设置为 True;当它遇到 -q 时,options.verbose 会被设置为 False

其他 action

optparse 支持的其他一些 action 是:

"store_const"

存储一个常量值,通过 Option.const 预设

"append"

将此选项的参数追加到一个列表中

"count"

将一个计数器加一

"callback"

调用一个指定的函数

这些将在 参考指南 部分和 选项回调 部分中介绍。

默认值

以上所有示例都涉及在看到某些命令行选项时设置某个变量(“目标”)。如果从未看到那些选项,会发生什么?由于我们没有提供任何默认值,它们都被设置为 None。这通常没问题,但有时你想要更多的控制。optparse 允许你为每个目标提供一个默认值,该值在解析命令行之前被赋值。

首先,考虑详细/安静的例子。如果我们希望 optparseverbose 设置为 True,除非看到 -q,那么我们可以这样做:

parser.add_option("-v", action="store_true", dest="verbose", default=True)
parser.add_option("-q", action="store_false", dest="verbose")

由于默认值适用于*目标*(destination)而不是任何特定的选项,并且这两个选项恰好有相同的目标,所以这完全等价:

parser.add_option("-v", action="store_true", dest="verbose")
parser.add_option("-q", action="store_false", dest="verbose", default=True)

考虑一下这个:

parser.add_option("-v", action="store_true", dest="verbose", default=False)
parser.add_option("-q", action="store_false", dest="verbose", default=True)

同样,verbose 的默认值将是 True:为任何特定目标提供的最后一个默认值是有效的。

一种更清晰的指定默认值的方法是使用 OptionParser 的 set_defaults() 方法,你可以在调用 parse_args() 之前的任何时候调用它:

parser.set_defaults(verbose=True)
parser.add_option(...)
(options, args) = parser.parse_args()

和之前一样,为给定选项目标指定的最后一个值是有效的。为清晰起见,尽量使用其中一种方法来设置默认值,而不是两者都用。

生成帮助信息

optparse 能够自动生成帮助和用法文本,这对于创建用户友好的命令行界面非常有用。你所要做的就是为每个选项提供一个 help 值,并可选地为你的整个程序提供一个简短的用法信息。这是一个用用户友好(有文档的)选项填充的 OptionParser:

usage = "usage: %prog [options] arg1 arg2"
parser = OptionParser(usage=usage)
parser.add_option("-v", "--verbose",
                  action="store_true", dest="verbose", default=True,
                  help="make lots of noise [default]")
parser.add_option("-q", "--quiet",
                  action="store_false", dest="verbose",
                  help="be vewwy quiet (I'm hunting wabbits)")
parser.add_option("-f", "--filename",
                  metavar="FILE", help="write output to FILE")
parser.add_option("-m", "--mode",
                  default="intermediate",
                  help="interaction mode: novice, intermediate, "
                       "or expert [default: %default]")

如果 optparse 在命令行上遇到 -h--help,或者如果你只是调用 parser.print_help(),它会向标准输出打印以下内容:

Usage: <yourscript> [options] arg1 arg2

Options:
  -h, --help            show this help message and exit
  -v, --verbose         make lots of noise [default]
  -q, --quiet           be vewwy quiet (I'm hunting wabbits)
  -f FILE, --filename=FILE
                        write output to FILE
  -m MODE, --mode=MODE  interaction mode: novice, intermediate, or
                        expert [default: intermediate]

(如果帮助输出是由帮助选项触发的,optparse 在打印帮助文本后会退出。)

这里有很多事情在帮助 optparse 生成尽可能好的帮助信息:

  • 脚本定义了自己的用法信息:

    usage = "usage: %prog [options] arg1 arg2"
    

    optparse 会将用法字符串中的 %prog 展开为当前程序的名称,即 os.path.basename(sys.argv[0])。然后,展开的字符串会在详细的选项帮助信息之前打印出来。

    如果你不提供用法字符串,optparse 会使用一个平淡但合理的默认值:"Usage: %prog [options]",如果你的脚本不接受任何位置参数,这是可以的。

  • 每个选项都定义了一个帮助字符串,并且不用担心换行问题——optparse 会负责换行并使帮助输出看起来很好。

  • 接受值的选项会在其自动生成的帮助信息中表明这一事实,例如对于“mode”选项:

    -m MODE, --mode=MODE
    

    在这里,“MODE”被称为元变量(meta-variable):它代表用户应该提供给 -m/--mode 的参数。默认情况下,optparse 会将目标变量名转换为大写,并将其用作元变量。有时,这并不是你想要的——例如,--filename 选项明确设置了 metavar="FILE",从而产生了这个自动生成的选项描述:

    -f FILE, --filename=FILE
    

    这不仅仅是为了节省空间,这一点很重要:手动编写的帮助文本使用元变量 FILE 来向用户暗示,半形式化语法 -f FILE 和非形式化语义描述“write output to FILE”之间存在联系。这是一种简单而有效的方法,可以使你的帮助文本对最终用户来说更加清晰和有用。

  • 具有默认值的选项可以在帮助字符串中包含 %default——optparse 会将其替换为选项默认值的 str()。如果一个选项没有默认值(或者默认值为 None),%default 会展开为 none

选项分组

当处理许多选项时,为了获得更好的帮助输出,将这些选项分组会很方便。一个 OptionParser 可以包含多个选项组,每个选项组都可以包含多个选项。

选项组是通过 OptionGroup 类获得的:

class optparse.OptionGroup(parser, title, description=None)

其中

  • parser 是该组将被插入的 OptionParser 实例

  • title 是组标题

  • description,可选,是对该组的长描述

OptionGroup 继承自 OptionContainer(就像 OptionParser 一样),因此可以使用 add_option() 方法向组中添加选项。

一旦所有选项都声明完毕,就可以使用 OptionParseradd_option_group() 方法将该组添加到先前定义的解析器中。

继续使用上一节定义的解析器,向解析器添加一个 OptionGroup 很简单:

group = OptionGroup(parser, "Dangerous Options",
                    "Caution: use these options at your own risk.  "
                    "It is believed that some of them bite.")
group.add_option("-g", action="store_true", help="Group option.")
parser.add_option_group(group)

这将导致以下帮助输出:

Usage: <yourscript> [options] arg1 arg2

Options:
  -h, --help            show this help message and exit
  -v, --verbose         make lots of noise [default]
  -q, --quiet           be vewwy quiet (I'm hunting wabbits)
  -f FILE, --filename=FILE
                        write output to FILE
  -m MODE, --mode=MODE  interaction mode: novice, intermediate, or
                        expert [default: intermediate]

  Dangerous Options:
    Caution: use these options at your own risk.  It is believed that some
    of them bite.

    -g                  Group option.

一个更完整的例子可能涉及使用多个组:仍然扩展前面的例子

group = OptionGroup(parser, "Dangerous Options",
                    "Caution: use these options at your own risk.  "
                    "It is believed that some of them bite.")
group.add_option("-g", action="store_true", help="Group option.")
parser.add_option_group(group)

group = OptionGroup(parser, "Debug Options")
group.add_option("-d", "--debug", action="store_true",
                 help="Print debug information")
group.add_option("-s", "--sql", action="store_true",
                 help="Print all SQL statements executed")
group.add_option("-e", action="store_true", help="Print every action done")
parser.add_option_group(group)

结果会是如下的输出:

Usage: <yourscript> [options] arg1 arg2

Options:
  -h, --help            show this help message and exit
  -v, --verbose         make lots of noise [default]
  -q, --quiet           be vewwy quiet (I'm hunting wabbits)
  -f FILE, --filename=FILE
                        write output to FILE
  -m MODE, --mode=MODE  interaction mode: novice, intermediate, or expert
                        [default: intermediate]

  Dangerous Options:
    Caution: use these options at your own risk.  It is believed that some
    of them bite.

    -g                  Group option.

  Debug Options:
    -d, --debug         Print debug information
    -s, --sql           Print all SQL statements executed
    -e                  Print every action done

另一个有趣的方法,特别是在以编程方式处理选项组时,是:

OptionParser.get_option_group(opt_str)

返回短选项或长选项字符串 *opt_str* (例如 '-o''--option') 所属的 OptionGroup。如果没有这样的 OptionGroup,则返回 None

打印版本字符串

与简短的用法字符串类似,optparse 也可以为你的程序打印一个版本字符串。你必须将该字符串作为 version 参数提供给 OptionParser:

parser = OptionParser(usage="%prog [-f] [-q]", version="%prog 1.0")

%prog 的展开方式与在 usage 中的一样。除此之外,version 可以包含任何你喜欢的内容。当你提供了它,optparse 会自动向你的解析器添加一个 --version 选项。如果它在命令行上遇到这个选项,它会展开你的 version 字符串(通过替换 %prog),将其打印到标准输出,然后退出。

例如,如果你的脚本名为 /usr/bin/foo

$ /usr/bin/foo --version
foo 1.0

以下两种方法可用于打印和获取 version 字符串:

OptionParser.print_version(file=None)

将当前程序的版本信息(self.version)打印到 *file*(默认为 stdout)。与 print_usage() 一样,self.version 中任何出现的 %prog 都会被替换为当前程序的名称。如果 self.version 为空或未定义,则不执行任何操作。

OptionParser.get_version()

print_version() 相同,但返回版本字符串而不是打印它。

optparse 如何处理错误

optparse 需要处理两大类错误:程序员错误和用户错误。程序员错误通常是错误地调用 OptionParser.add_option(),例如无效的选项字符串、未知的选项属性、缺失的选项属性等。这些错误按常规方式处理:引发一个异常(optparse.OptionErrorTypeError),让程序崩溃。

处理用户错误要重要得多,因为无论你的代码多稳定,这些错误都保证会发生。optparse 可以自动检测一些用户错误,例如错误的选项参数(在 -n 需要整数参数的地方传递 -n 4x)、缺失的参数(-n 在命令行末尾,而 -n 需要任何类型的参数)。此外,你可以调用 OptionParser.error() 来表示一个应用程序定义的错误条件:

(options, args) = parser.parse_args()
...
if options.a and options.b:
    parser.error("options -a and -b are mutually exclusive")

在任何一种情况下,optparse 都以同样的方式处理错误:它将程序的用法信息和错误消息打印到标准错误,并以错误状态码 2 退出。

考虑上面的第一个例子,用户给一个需要整数的选项传递了 4x

$ /usr/bin/foo -n 4x
Usage: foo [options]

foo: error: option -n: invalid integer value: '4x'

或者,用户根本没有传递值:

$ /usr/bin/foo -n
Usage: foo [options]

foo: error: -n option requires an argument

optparse 生成的错误消息总是会提及涉及错误的选项;确保在从你的应用程序代码调用 OptionParser.error() 时也这样做。

如果 optparse 的默认错误处理行为不符合你的需求,你需要子类化 OptionParser 并重写其 exit() 和/或 error() 方法。

整合示例

基于 optparse 的脚本通常看起来是这样的:

from optparse import OptionParser
...
def main():
    usage = "usage: %prog [options] arg"
    parser = OptionParser(usage)
    parser.add_option("-f", "--file", dest="filename",
                      help="read data from FILENAME")
    parser.add_option("-v", "--verbose",
                      action="store_true", dest="verbose")
    parser.add_option("-q", "--quiet",
                      action="store_false", dest="verbose")
    ...
    (options, args) = parser.parse_args()
    if len(args) != 1:
        parser.error("incorrect number of arguments")
    if options.verbose:
        print("reading %s..." % options.filename)
    ...

if __name__ == "__main__":
    main()

参考指南

创建解析器

使用 optparse 的第一步是创建一个 OptionParser 实例。

class optparse.OptionParser(...)

OptionParser 构造函数没有必需的参数,但有许多可选的关键字参数。你应该总是以关键字参数的形式传递它们,即不要依赖于参数声明的顺序。

usage (默认: "%prog [options]")

当你的程序运行不正确或带有帮助选项时打印的用法摘要。当 optparse 打印用法字符串时,它会把 %prog 展开成 os.path.basename(sys.argv[0])(或者如果你传递了 prog 关键字参数,则展开成 prog)。要抑制用法信息,请传递特殊值 optparse.SUPPRESS_USAGE

option_list (默认: [])

用于填充解析器的 Option 对象列表。option_list 中的选项会在 standard_option_list(一个可以由 OptionParser 子类设置的类属性)中的任何选项之后,但在任何版本或帮助选项之前添加。已弃用;请在创建解析器后使用 add_option() 代替。

option_class (默认: optparse.Option)

add_option() 中向解析器添加选项时使用的类。

version (默认: None)

当用户提供版本选项时打印的版本字符串。如果你为 version 提供了一个真值,optparse 会自动添加一个带有单个选项字符串 --version 的版本选项。子字符串 %prog 的展开方式与 usage 相同。

conflict_handler (默认: "error")

指定当向解析器添加具有冲突选项字符串的选项时该怎么做;参见 选项之间的冲突 部分。

description (默认: None)

一段简要概述你程序的文本。optparse 会重新格式化这段文本以适应当前终端的宽度,并在用户请求帮助时打印它(在 usage 之后,但在选项列表之前)。

formatter (默认: 一个新的 IndentedHelpFormatter)

一个 optparse.HelpFormatter 的实例,将用于打印帮助文本。optparse 为此目的提供了两个具体类:IndentedHelpFormatter 和 TitledHelpFormatter。

add_help_option (默认值: True)

如果为 true,optparse 将向解析器添加一个帮助选项(选项字符串为 -h--help)。

prog

在展开 usageversion 中的 %prog 时使用的字符串,而不是使用 os.path.basename(sys.argv[0])

epilog (默认: None)

在选项帮助信息之后打印的一段帮助文本。

填充解析器

有几种方法可以用选项来填充解析器。首选的方法是使用 OptionParser.add_option(),如 教程 部分所示。add_option() 可以通过以下两种方式之一调用:

  • 传递一个 Option 实例(由 make_option() 返回)

  • 传递任何可接受的 make_option()(即 Option 构造函数)的位置参数和关键字参数的组合,它会为你创建 Option 实例

另一种选择是向 OptionParser 构造函数传递一个预先构建的 Option 实例列表,如下所示:

option_list = [
    make_option("-f", "--filename",
                action="store", type="string", dest="filename"),
    make_option("-q", "--quiet",
                action="store_false", dest="verbose"),
    ]
parser = OptionParser(option_list=option_list)

(make_option() 是一个用于创建 Option 实例的工厂函数;目前它是 Option 构造函数的别名。未来版本的 optparse 可能会将 Option 分成几个类,make_option() 将选择正确的类来实例化。不要直接实例化 Option。)

定义选项

每个 Option 实例代表一组同义的命令行选项字符串,例如 -f--file。你可以指定任意数量的短选项或长选项字符串,但必须至少指定一个总的选项字符串。

创建 Option 实例的规范方式是使用 OptionParseradd_option() 方法。

OptionParser.add_option(option)
OptionParser.add_option(*opt_str, attr=value, ...)

要定义一个只有短选项字符串的选项:

parser.add_option("-f", attr=value, ...)

要定义一个只有长选项字符串的选项:

parser.add_option("--foo", attr=value, ...)

关键字参数定义了新 Option 对象的属性。最重要的选项属性是 action,它在很大程度上决定了哪些其他属性是相关的或必需的。如果你传递了不相关的选项属性,或者未能传递必需的属性,optparse 会引发一个 OptionError 异常来解释你的错误。

一个选项的 *action* 决定了 optparse 在命令行中遇到此选项时会做什么。硬编码在 optparse 中的标准选项 action 有:

"store"

存储此选项的参数(默认)

"store_const"

存储一个常量值,通过 Option.const 预设

"store_true"

存储 True

"store_false"

存储 False

"append"

将此选项的参数追加到一个列表中

"append_const"

将一个常量值追加到一个列表中,该常量通过 Option.const 预先设置

"count"

将一个计数器加一

"callback"

调用一个指定的函数

"help"

打印一条用法信息,包括所有选项及其文档

(如果你不提供 action,默认为 "store"。对于此 action,你还可以提供 typedest 选项属性;请参阅 标准选项动作。)

如你所见,大多数 action 都涉及在某处存储或更新值。optparse 总是为此创建一个特殊的对象,通常称为 options,它是 optparse.Values 的一个实例。

class optparse.Values

一个将已解析的参数名称和值作为属性来保存的对象。通常在调用 OptionParser.parse_args() 时被创建,并可以通过传递给 OptionParser.parse_args()values 参数的自定义子类来覆盖(如 解析参数 中所述)。

选项参数(以及其他各种值)根据 dest(目标)选项属性,作为该对象的属性进行存储。

例如,当你调用

parser.parse_args()

optparse 首先要做的事情之一就是创建 options 对象。

options = Values()

如果此解析器中的一个选项定义为

parser.add_option("-f", "--file", action="store", type="string", dest="filename")

并且正在解析的命令行包含以下任何一种

-ffoo
-f foo
--file=foo
--file foo

那么 optparse 在看到这个选项时,会执行与下面等价的操作:

options.filename = "foo"

typedest 选项属性几乎与 action 一样重要,但只有 action 对*所有*选项都有意义。

选项属性

class optparse.Option

单个命令行参数,其各种属性通过关键字参数传递给构造函数。通常通过 OptionParser.add_option() 创建而不是直接创建,并且可以通过传递给 OptionParseroption_class 参数的自定义类来覆盖。

以下选项属性可以作为关键字参数传递给 OptionParser.add_option()。如果你传递了一个与特定选项无关的选项属性,或者未能传递一个必需的选项属性,optparse 会引发 OptionError

Option.action

(默认值:"store"

确定当在命令行上看到此选项时 optparse 的行为;可用的选项记录在这里

Option.type

(默认值:"string"

此选项期望的参数类型(例如 "string""int");可用的选项类型记录在这里

Option.dest

(默认值:从选项字符串派生)

如果选项的动作意味着在某处写入或修改一个值,这个属性会告诉 optparse 写入的位置:dest 指定了 options 对象的一个属性,optparse 在解析命令行时会构建这个对象。

Option.default

如果在命令行中没有看到该选项,则用于此选项目标的值。另请参阅 OptionParser.set_defaults()

Option.nargs

(默认值:1)

当看到此选项时,应使用多少个 type 类型的参数。如果 > 1,optparse 会将一个值的元组存储到 dest

Option.const

对于存储常量值的动作,这是要存储的常量值。

Option.choices

对于类型为 "choice" 的选项,这是用户可以选择的字符串列表。

Option.callback

对于 action 为 "callback" 的选项,这是看到该选项时要调用的可调用对象。有关传递给可调用对象的参数的详细信息,请参阅 选项回调 部分。

Option.callback_args
Option.callback_kwargs

在四个标准回调参数之后,传递给 callback 的额外位置参数和关键字参数。

Option.help

当用户提供 help 选项(例如 --help)后,列出所有可用选项时,为该选项打印的帮助文本。如果未提供帮助文本,该选项将被列出但没有帮助文本。要隐藏此选项,请使用特殊值 optparse.SUPPRESS_HELP

Option.metavar

(默认值:从选项字符串派生)

打印帮助文本时用作选项参数的占位符。示例请参见 教程 部分。

标准选项动作

各种选项动作都有略微不同的要求和效果。大多数动作有几个相关的选项属性,你可以指定它们来指导 optparse 的行为;少数动作有必需的属性,你必须为任何使用该动作的选项指定这些属性。

  • "store" [相关:type, dest, nargs, choices]

    该选项后必须跟一个参数,该参数会根据 type 转换为一个值并存储在 dest 中。如果 nargs > 1,将从命令行使用多个参数;所有参数都将根据 type 进行转换,并以元组形式存储到 dest。请参阅 标准选项类型 部分。

    如果提供了 choices(一个字符串列表或元组),类型默认为 "choice"

    如果未提供 type,则默认为 "string"

    如果未提供 destoptparse 会从第一个长选项字符串派生一个目标(例如,--foo-bar 意味着 foo_bar)。如果没有长选项字符串,optparse 会从第一个短选项字符串派生一个目标(例如,-f 意味着 f)。

    示例

    parser.add_option("-f")
    parser.add_option("-p", type="float", nargs=3, dest="point")
    

    当它解析命令行时

    -f foo.txt -p 1 -3.5 4 -fbar.txt
    

    optparse 将会设置

    options.f = "foo.txt"
    options.point = (1.0, -3.5, 4.0)
    options.f = "bar.txt"
    
  • "store_const" [必需:const;相关:dest]

    const 被存储在 dest 中。

    示例

    parser.add_option("-q", "--quiet",
                      action="store_const", const=0, dest="verbose")
    parser.add_option("-v", "--verbose",
                      action="store_const", const=1, dest="verbose")
    parser.add_option("--noisy",
                      action="store_const", const=2, dest="verbose")
    

    如果看到 --noisyoptparse 将设置

    options.verbose = 2
    
  • "store_true" [相关: dest]

    "store_const" 的一种特殊情况,它将 True 存储到 dest

  • "store_false" [相关: dest]

    类似于 "store_true",但存储的是 False

    示例

    parser.add_option("--clobber", action="store_true", dest="clobber")
    parser.add_option("--no-clobber", action="store_false", dest="clobber")
    
  • "append" [相关:typedestnargschoices]

    该选项后必须跟一个参数,该参数会附加到 dest 中的列表中。如果未提供 dest 的默认值,当 optparse 首次在命令行上遇到此选项时,会自动创建一个空列表。如果 nargs > 1,则会使用多个参数,并且一个长度为 nargs 的元组将被附加到 dest

    typedest 的默认值与 "store" 动作相同。

    示例

    parser.add_option("-t", "--tracks", action="append", type="int")
    

    如果在命令行中看到 -t3optparse 会执行以下等效操作:

    options.tracks = []
    options.tracks.append(int("3"))
    

    如果稍后看到 --tracks=4,它会执行:

    options.tracks.append(int("4"))
    

    append 动作会在选项的当前值上调用 append 方法。这意味着指定的任何默认值都必须有一个 append 方法。这也意味着如果默认值非空,那么默认元素将存在于该选项的解析值中,并且任何来自命令行的值都会附加在这些默认值之后。

    >>> parser.add_option("--files", action="append", default=['~/.mypkg/defaults'])
    >>> opts, args = parser.parse_args(['--files', 'overrides.mypkg'])
    >>> opts.files
    ['~/.mypkg/defaults', 'overrides.mypkg']
    
  • "append_const" [必需:const;相关:dest]

    "store_const" 类似,但是值 const 会被追加到 dest;与 "append" 一样,dest 默认为 None,并且在第一次遇到该选项时会自动创建一个空列表。

  • "count" [相关: dest]

    将存储在 dest 的整数加一。如果没有提供默认值,dest 在第一次递增前会被设置为零。

    示例

    parser.add_option("-v", action="count", dest="verbosity")
    

    第一次在命令行中看到 -v 时,optparse 会执行与下面等效的操作:

    options.verbosity = 0
    options.verbosity += 1
    

    之后每次出现 -v 都会导致:

    options.verbosity += 1
    
  • "callback" [必需:callback;相关:typenargscallback_argscallback_kwargs]

    调用由 callback 指定的函数,该函数的调用方式为:

    func(option, opt_str, value, parser, *args, **kwargs)
    

    更多细节请参阅 选项回调 一节。

  • "help"

    为当前选项解析器中的所有选项打印完整的帮助信息。帮助信息由传递给 OptionParser 构造函数的 usage 字符串和传递给每个选项的 help 字符串构成。

    如果某个选项没有提供 help 字符串,它仍会列在帮助信息中。要完全忽略一个选项,请使用特殊值 optparse.SUPPRESS_HELP

    optparse 会自动为所有 OptionParser 添加一个 help 选项,所以你通常不需要自己创建一个。

    示例

    from optparse import OptionParser, SUPPRESS_HELP
    
    # usually, a help option is added automatically, but that can
    # be suppressed using the add_help_option argument
    parser = OptionParser(add_help_option=False)
    
    parser.add_option("-h", "--help", action="help")
    parser.add_option("-v", action="store_true", dest="verbose",
                      help="Be moderately verbose")
    parser.add_option("--file", dest="filename",
                      help="Input file to read data from")
    parser.add_option("--secret", help=SUPPRESS_HELP)
    

    如果 optparse 在命令行中看到 -h--help,它会向标准输出打印类似下面的帮助信息(假设 sys.argv[0]"foo.py"):

    Usage: foo.py [options]
    
    Options:
      -h, --help        Show this help message and exit
      -v                Be moderately verbose
      --file=FILENAME   Input file to read data from
    

    打印完帮助信息后,optparse 会用 sys.exit(0) 终止你的进程。

  • "version"

    将提供给 OptionParser 的版本号打印到标准输出并退出。版本号实际上是由 OptionParser 的 print_version() 方法格式化和打印的。通常只在向 OptionParser 构造函数提供了 version 参数时才相关。与 help 选项一样,你很少需要创建 version 选项,因为 optparse 在需要时会自动添加它们。

标准选项类型

optparse 有五个内置的选项类型:"string""int""choice""float""complex"。如果你需要添加新的选项类型,请参见 扩展 optparse 一节。

字符串选项的参数不会以任何方式进行检查或转换:命令行上的文本会按原样存储到目标中(或传递给回调函数)。

整数参数("int" 类型)的解析方式如下:

  • 如果数字以 0x 开头,则解析为十六进制数

  • 如果数字以 0 开头,则解析为八进制数

  • 如果数字以 0b 开头,则解析为二进制数

  • 否则,数字被解析为十进制数

转换是通过使用适当的基数(2、8、10 或 16)调用 int() 来完成的。如果此操作失败,optparse 也会失败,但会提供更有用的错误消息。

"float""complex" 选项参数直接用 float()complex() 进行转换,错误处理方式类似。

"choice" 选项是 "string" 选项的子类型。choices 选项属性(一个字符串序列)定义了允许的选项参数集合。optparse.check_choice() 将用户提供的选项参数与此主列表进行比较,如果给出无效字符串,则引发 OptionValueError

解析参数

创建和填充 OptionParser 的全部意义在于调用其 parse_args() 方法。

OptionParser.parse_args(args=None, values=None)

解析在 args 中找到的命令行选项。

输入参数是:

args

要处理的参数列表(默认:sys.argv[1:]

一个用于存储选项参数的 Values 对象(默认:Values 的一个新实例)——如果你提供一个现有对象,选项的默认值将不会在其上初始化。

返回值是一对 (options, args),其中

options

作为 values 传入的同一个对象,或者由 optparse 创建的 optparse.Values 实例。

args

处理完所有选项后剩余的位置参数

最常见的用法是不提供任何关键字参数。如果你提供了 values,它将被重复的 setattr() 调用(大致上,每个存储到选项目标的选项参数对应一次调用)修改,并由 parse_args() 返回。

如果 parse_args() 在参数列表中遇到任何错误,它会调用 OptionParser 的 error() 方法,并附带一条适合最终用户的错误消息。这最终会以退出状态 2(传统的 Unix 命令行错误退出状态)终止你的进程。

查询和操作你的选项解析器

选项解析器的默认行为可以略微定制,你也可以探查你的选项解析器,看看里面有什么。OptionParser 提供了几种方法来帮助你:

OptionParser.disable_interspersed_args()

设置解析在遇到第一个非选项参数时停止。例如,如果 -a-b 都是不带参数的简单选项,optparse 通常接受这种语法:

prog -a arg1 -b arg2

并将其视为等同于

prog -a -b arg1 arg2

要禁用此功能,请调用 disable_interspersed_args()。这将恢复传统的 Unix 语法,即选项解析在遇到第一个非选项参数时停止。

如果你有一个命令处理器,它运行另一个有自己选项的命令,并且你希望确保这些选项不会混淆,就可以使用这个方法。例如,每个命令可能有不同的选项集。

OptionParser.enable_interspersed_args()

设置解析在遇到第一个非选项时不要停止,允许开关与命令参数交错。这是默认行为。

OptionParser.get_option(opt_str)

返回带有选项字符串 opt_str 的 Option 实例,如果没有选项具有该选项字符串,则返回 None

OptionParser.has_option(opt_str)

如果 OptionParser 有一个带有选项字符串 opt_str(例如 -q--verbose)的选项,则返回 True

OptionParser.remove_option(opt_str)

如果 OptionParser 有一个对应于 opt_str 的选项,该选项将被移除。如果该选项提供了任何其他选项字符串,所有这些选项字符串都将变得无效。如果 opt_str 没有出现在属于此 OptionParser 的任何选项中,则引发 ValueError

选项之间的冲突

如果你不小心,很容易定义出选项字符串冲突的选项:

parser.add_option("-n", "--dry-run", ...)
...
parser.add_option("-n", "--noisy", ...)

(如果你定义了自己带有某些标准选项的 OptionParser 子类,这种情况尤其常见。)

每次添加一个选项时,optparse 都会检查与现有选项的冲突。如果发现任何冲突,它会调用当前的冲突处理机制。你可以在构造函数中设置冲突处理机制:

parser = OptionParser(..., conflict_handler=handler)

或通过单独的调用:

parser.set_conflict_handler(handler)

可用的冲突处理器有:

"error" (默认)

假定选项冲突是编程错误,并引发 OptionConflictError

"resolve"

智能地解决选项冲突(见下文)

举个例子,让我们定义一个能够智能解决冲突的 OptionParser,并向其中添加冲突的选项:

parser = OptionParser(conflict_handler="resolve")
parser.add_option("-n", "--dry-run", ..., help="do no harm")
parser.add_option("-n", "--noisy", ..., help="be noisy")

此时,optparse 检测到先前添加的选项已经在使用 -n 选项字符串。由于 conflict_handler"resolve",它通过从先前选项的选项字符串列表中移除 -n 来解决此问题。现在,--dry-run 是用户激活该选项的唯一方式。如果用户请求帮助,帮助信息将反映这一点:

Options:
  --dry-run     do no harm
  ...
  -n, --noisy   be noisy

有可能将先前添加的选项的选项字符串逐渐削减,直到一个不剩,导致用户无法从命令行调用该选项。在这种情况下,optparse 会完全移除该选项,使其不会出现在帮助文本或其他任何地方。继续我们现有的 OptionParser:

parser.add_option("--dry-run", ..., help="new dry-run option")

此时,原来的 -n/--dry-run 选项已无法访问,所以 optparse 将其移除,留下如下帮助文本:

Options:
  ...
  -n, --noisy   be noisy
  --dry-run     new dry-run option

清理

OptionParser 实例存在一些循环引用。这对于 Python 的垃圾回收器来说应该不是问题,但你可能希望通过在你用完 OptionParser 后调用其 destroy() 方法来显式地打破循环引用。这在长时运行的应用中尤其有用,因为这些应用中可能会有从你的 OptionParser 可达的大型对象图。

其他方法

OptionParser 支持其他几个公共方法:

OptionParser.set_usage(usage)

根据上面为 usage 构造函数关键字参数描述的规则来设置用法字符串。传递 None 会设置默认的用法字符串;使用 optparse.SUPPRESS_USAGE 来抑制用法消息。

OptionParser.print_usage(file=None)

将当前程序的用法信息(self.usage)打印到 file(默认为标准输出)。self.usage 中任何出现的字符串 %prog 都会被替换为当前程序的名称。如果 self.usage 为空或未定义,则不执行任何操作。

OptionParser.get_usage()

print_usage() 相同,但返回用法字符串而不是打印它。

OptionParser.set_defaults(dest=value, ...)

一次性为多个选项目标设置默认值。使用 set_defaults() 是设置选项默认值的首选方式,因为多个选项可以共享同一个目标。例如,如果几个“模式”选项都设置了同一个目标,它们中的任何一个都可以设置默认值,而最后一个会生效:

parser.add_option("--advanced", action="store_const",
                  dest="mode", const="advanced",
                  default="novice")    # overridden below
parser.add_option("--novice", action="store_const",
                  dest="mode", const="novice",
                  default="advanced")  # overrides above setting

为避免这种混淆,请使用 set_defaults()

parser.set_defaults(mode="advanced")
parser.add_option("--advanced", action="store_const",
                  dest="mode", const="advanced")
parser.add_option("--novice", action="store_const",
                  dest="mode", const="novice")

选项回调

optparse 的内置动作和类型不足以满足你的需求时,你有两个选择:扩展 optparse 或定义一个回调选项。扩展 optparse 更为通用,但对于许多简单情况来说有些小题大做。通常,一个简单的回调就足够了。

定义一个回调选项有两个步骤:

  • 使用 "callback" 动作定义选项本身

  • 编写回调函数;这是一个至少接受四个参数的函数(或方法),如下文所述

定义回调选项

与往常一样,定义回调选项最简单的方法是使用 OptionParser.add_option() 方法。除了 action,你必须指定的唯一选项属性是 callback,即要调用的函数:

parser.add_option("-c", action="callback", callback=my_callback)

callback 是一个函数(或其他可调用对象),所以当你创建这个回调选项时,你必须已经定义了 my_callback()。在这个简单的例子中,optparse 甚至不知道 -c 是否接受任何参数,这通常意味着该选项不接受参数——仅仅是 -c 在命令行上的出现就足够了。然而,在某些情况下,你可能希望你的回调函数消费任意数量的命令行参数。这就是编写回调变得棘手的地方;本节稍后会讨论。

optparse 总是向你的回调函数传递四个特定的参数,只有在你通过 callback_argscallback_kwargs 指定时,它才会传递额外的参数。因此,最小的回调函数签名是:

def my_callback(option, opt, value, parser):

回调的四个参数将在下面描述。

在定义回调选项时,你还可以提供其他几个选项属性:

type

具有其通常的含义:与 "store""append" 动作一样,它指示 optparse 使用一个参数并将其转换为 type。然而,optparse 并不会将转换后的值存储在任何地方,而是将其传递给你的回调函数。

nargs

也具有其通常的含义:如果提供了该属性且 > 1,optparse 将使用 nargs 个参数,每个参数都必须能转换为 type。然后,它会将一个包含转换后值的元组传递给你的回调函数。

callback_args

一个要传递给回调函数的额外位置参数的元组

callback_kwargs

一个要传递给回调函数的额外关键字参数的字典

回调函数如何被调用

所有回调函数都按以下方式调用:

func(option, opt_str, value, parser, *args, **kwargs)

其中

选项

是正在调用回调的 Option 实例

opt_str

是在命令行中看到的、触发回调的选项字符串。(如果使用了缩写的长选项,opt_str 将是完整的、规范的选项字符串——例如,如果用户在命令行中输入 --foo 作为 --foobar 的缩写,那么 opt_str 将是 "--foobar"。)

value (值)

是在命令行中看到的此选项的参数。optparse 只有在设置了 type 时才会期望一个参数;value 的类型将是选项类型所隐含的类型。如果此选项的 typeNone(不期望参数),那么 value 将是 None。如果 nargs > 1,value 将是一个包含适当类型值的元组。

解析器

是驱动整个过程的 OptionParser 实例,主要有用,因为你可以通过其实例属性访问一些其他有趣的数据:

parser.largs

当前剩余参数的列表,即那些已被处理但既非选项也非选项参数的参数。你可以随意修改 parser.largs,例如向其中添加更多参数。(这个列表将成为 parse_args() 的第二个返回值 args。)

parser.rargs

当前剩余参数的列表,即移除了 opt_strvalue(如果适用)之后,只剩下它们后面的参数。你可以随意修改 parser.rargs,例如通过消耗更多参数。

parser.values

默认存储选项值的对象(optparse.OptionValues 的实例)。这让回调函数可以使用与 optparse 其余部分相同的机制来存储选项值;你不需要处理全局变量或闭包。你还可以访问或修改在命令行上已遇到的任何选项的值。

args

是通过 callback_args 选项属性提供的任意位置参数的元组。

kwargs

是通过 callback_kwargs 提供的任意关键字参数的字典。

在回调中引发错误

如果选项或其参数有任何问题,回调函数应该引发 OptionValueErroroptparse 会捕获这个异常并终止程序,将你提供的错误消息打印到标准错误输出。你的消息应该清晰、简洁、准确,并提及出错的选项。否则,用户将很难弄清楚他们做错了什么。

回调示例 1:简单的回调

这是一个不带参数的回调选项示例,它只是记录下该选项已被看到:

def record_foo_seen(option, opt_str, value, parser):
    parser.values.saw_foo = True

parser.add_option("--foo", action="callback", callback=record_foo_seen)

当然,你可以用 "store_true" 动作来做到这一点。

回调示例 2:检查选项顺序

这是一个稍微有趣的例子:记录 -a 被看到的事实,但如果它在命令行中出现在 -b 之后,就报错。

def check_order(option, opt_str, value, parser):
    if parser.values.b:
        raise OptionValueError("can't use -a after -b")
    parser.values.a = 1
...
parser.add_option("-a", action="callback", callback=check_order)
parser.add_option("-b", action="store_true", dest="b")

回调示例 3:检查选项顺序(通用化)

如果你想为几个相似的选项重用这个回调(设置一个标志,但如果 -b 已经出现过就报错),就需要做一些工作:错误消息和它设置的标志必须被通用化。

def check_order(option, opt_str, value, parser):
    if parser.values.b:
        raise OptionValueError("can't use %s after -b" % opt_str)
    setattr(parser.values, option.dest, 1)
...
parser.add_option("-a", action="callback", callback=check_order, dest='a')
parser.add_option("-b", action="store_true", dest="b")
parser.add_option("-c", action="callback", callback=check_order, dest='c')

回调示例 4:检查任意条件

当然,你可以在那里设置任何条件——你不仅限于检查已定义选项的值。例如,如果你有一些选项不应该在月圆时调用,你只需要这样做:

def check_moon(option, opt_str, value, parser):
    if is_moon_full():
        raise OptionValueError("%s option invalid when moon is full"
                               % opt_str)
    setattr(parser.values, option.dest, 1)
...
parser.add_option("--foo",
                  action="callback", callback=check_moon, dest="foo")

is_moon_full() 的定义留给读者作为练习。)

回调示例 5:固定参数

当你定义需要固定数量参数的回调选项时,事情会变得稍微有趣一些。指定回调选项接受参数类似于定义一个 "store""append" 选项:如果你定义了 type,那么该选项接受一个必须能转换为该类型的参数;如果你进一步定义了 nargs,那么该选项接受 nargs 个参数。

这是一个模拟标准 "store" 动作的例子:

def store_value(option, opt_str, value, parser):
    setattr(parser.values, option.dest, value)
...
parser.add_option("--foo",
                  action="callback", callback=store_value,
                  type="int", nargs=3, dest="foo")

请注意,optparse 会负责为你处理 3 个参数的消耗并将它们转换为整数;你所要做的就是存储它们。(或者做任何事;显然,对于这个例子你不需要回调。)

回调示例 6:可变参数

当你想让一个选项接受可变数量的参数时,事情就变得棘手了。对于这种情况,你必须编写一个回调函数,因为 optparse 没有为此提供任何内置功能。而且你必须处理一些传统的 Unix 命令行解析的复杂细节,而这些细节通常由 optparse 为你处理。特别是,回调函数应实现对裸 --- 参数的常规规则:

  • --- 都可以是选项参数

  • --(如果不是某个选项的参数):停止命令行处理并丢弃 --

  • -(如果不是某个选项的参数):停止命令行处理但保留 -(将其附加到 parser.largs

如果你想要一个接受可变数量参数的选项,有几个微妙、棘手的问题需要担心。你选择的具体实现将取决于你愿意为你的应用程序做出哪些权衡(这也是为什么 optparse 不直接支持这种功能的原因)。

尽管如此,这里还是尝试为带可变参数的选项编写一个回调函数:

def vararg_callback(option, opt_str, value, parser):
    assert value is None
    value = []

    def floatable(str):
        try:
            float(str)
            return True
        except ValueError:
            return False

    for arg in parser.rargs:
        # stop on --foo like options
        if arg[:2] == "--" and len(arg) > 2:
            break
        # stop on -a, but not on -3 or -3.0
        if arg[:1] == "-" and len(arg) > 1 and not floatable(arg):
            break
        value.append(arg)

    del parser.rargs[:len(value)]
    setattr(parser.values, option.dest, value)

...
parser.add_option("-c", "--callback", dest="vararg_attr",
                  action="callback", callback=vararg_callback)

扩展 optparse

由于 optparse 解释命令行选项的两个主要控制因素是每个选项的动作和类型,所以最可能的扩展方向是添加新的动作和新的类型。

添加新类型

要添加新类型,你需要定义自己的 optparseOption 类的子类。这个类有几个属性定义了 optparse 的类型:TYPESTYPE_CHECKER

Option.TYPES

一个类型名称的元组;在你的子类中,只需定义一个新的元组 TYPES,它建立在标准元组的基础上。

Option.TYPE_CHECKER

一个将类型名称映射到类型检查函数的字典。类型检查函数具有以下签名:

def check_mytype(option, opt, value)

其中 option 是一个 Option 实例,opt 是一个选项字符串(例如 -f),而 value 是来自命令行的字符串,需要被检查并转换为你期望的类型。check_mytype() 应该返回一个假设类型为 mytype 的对象。类型检查函数返回的值最终会出现在 OptionParser.parse_args() 返回的 OptionValues 实例中,或者作为 value 参数传递给回调函数。

如果遇到任何问题,你的类型检查函数应该引发 OptionValueErrorOptionValueError 接受一个字符串参数,该参数会原封不动地传递给 OptionParsererror() 方法,该方法会依次在前面加上程序名称和字符串 "error:",然后将所有内容打印到标准错误输出,并终止进程。

这里有一个愚蠢的例子,演示了如何添加一个 "complex" 选项类型来解析命令行上的 Python 风格复数。(这个例子比以前更愚蠢了,因为 optparse 1.3 已经添加了对复数的内置支持,但没关系。)

首先,导入必要的模块

from copy import copy
from optparse import Option, OptionValueError

你需要先定义你的类型检查器,因为它稍后会被引用(在你的 Option 子类的 TYPE_CHECKER 类属性中)。

def check_complex(option, opt, value):
    try:
        return complex(value)
    except ValueError:
        raise OptionValueError(
            "option %s: invalid complex value: %r" % (opt, value))

最后,是 Option 子类

class MyOption (Option):
    TYPES = Option.TYPES + ("complex",)
    TYPE_CHECKER = copy(Option.TYPE_CHECKER)
    TYPE_CHECKER["complex"] = check_complex

(如果我们没有创建 Option.TYPE_CHECKERcopy(),我们最终会修改 optparse 的 Option 类的 TYPE_CHECKER 属性。因为这是 Python,除了良好的编程习惯和常识之外,没有什么能阻止你这样做。)

就是这样!现在你可以编写一个使用新选项类型的脚本,就像其他任何基于 optparse 的脚本一样,只是你需要指示你的 OptionParser 使用 MyOption 而不是 Option。

parser = OptionParser(option_class=MyOption)
parser.add_option("-c", type="complex")

或者,你可以构建自己的选项列表并将其传递给 OptionParser;如果你不像上面那样使用 add_option(),你就不需要告诉 OptionParser 使用哪个选项类。

option_list = [MyOption("-c", action="store", type="complex", dest="c")]
parser = OptionParser(option_list=option_list)

添加新的 action

添加新的 action 有点棘手,因为你需要理解 optparse 对 action 有几种分类。

“store” action

这些 action 会导致 optparse 将一个值存储到当前 OptionValues 实例的一个属性中;这些选项要求在 Option 构造函数中提供一个 dest 属性。

“typed” action

这些 action 从命令行接收一个值,并期望它是某种特定类型;或者说,是一个可以转换为特定类型的字符串。这些选项要求在 Option 构造函数中提供一个 type 属性。

这些集合是重叠的:一些默认的 “store” action 包括 "store""store_const""append""count",而默认的 “typed” action 包括 "store""append""callback"

当你添加一个 action 时,你需要通过在 Option 的以下至少一个类属性中列出它来进行分类(所有这些属性都是字符串列表):

Option.ACTIONS

所有的 action 都必须在 ACTIONS 中列出。

Option.STORE_ACTIONS

“store” action 还会在这里列出。

Option.TYPED_ACTIONS

“typed” action 还会在这里列出。

Option.ALWAYS_TYPED_ACTIONS

总是需要一个类型的 action(即其选项总是需要一个值)会在这里列出。这样做的唯一效果是,optparse 会为那些没有显式类型但其 action 在 ALWAYS_TYPED_ACTIONS 中列出的选项分配默认类型 "string"

为了真正实现你的新 action,你必须重写 Option 的 take_action() 方法,并添加一个识别你的 action 的分支。

例如,我们来添加一个 "extend" action。这类似于标准的 "append" action,但 "extend" 不是从命令行接收单个值并将其附加到现有列表中,而是会从一个逗号分隔的字符串中接收多个值,并用它们来扩展一个现有的列表。也就是说,如果 --names 是一个类型为 "string""extend" 选项,那么命令行:

--names=foo,bar --names blah --names ding,dong

将得到一个列表

["foo", "bar", "blah", "ding", "dong"]

我们再次定义一个 Option 的子类

class MyOption(Option):

    ACTIONS = Option.ACTIONS + ("extend",)
    STORE_ACTIONS = Option.STORE_ACTIONS + ("extend",)
    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("extend",)
    ALWAYS_TYPED_ACTIONS = Option.ALWAYS_TYPED_ACTIONS + ("extend",)

    def take_action(self, action, dest, opt, value, values, parser):
        if action == "extend":
            lvalue = value.split(",")
            values.ensure_value(dest, []).extend(lvalue)
        else:
            Option.take_action(
                self, action, dest, opt, value, values, parser)

值得注意的特性

  • "extend" 既期望在命令行上有一个值,又会将该值存储到某处,所以它同时出现在 STORE_ACTIONSTYPED_ACTIONS 中。

  • 为了确保 optparse"extend" action 分配默认类型 "string",我们也将 "extend" action 放在 ALWAYS_TYPED_ACTIONS 中。

  • MyOption.take_action() 只实现了这一个新 action,并将控制权交回给 Option.take_action() 来处理标准的 optparse action。

  • values 是 optparse_parser.Values 类的一个实例,它提供了一个非常有用的 ensure_value() 方法。ensure_value() 本质上是带有一个安全阀的 getattr();它的调用方式如下:

    values.ensure_value(attr, value)
    

    如果 valuesattr 属性不存在或者是 None,那么 ensure_value() 会先将其设置为 value,然后返回 value。这对于像 "extend""append""count" 这样的 action 非常方便,它们都在一个变量中累积数据,并期望该变量是某种特定类型(前两者是列表,后者是整数)。使用 ensure_value() 意味着使用你的 action 的脚本不必担心为相关选项目标设置默认值;它们可以简单地将默认值保留为 None,而 ensure_value() 会在需要时负责将其设置正确。

异常

exception optparse.OptionError

当创建一个 Option 实例时,如果参数无效或不一致,则会引发此异常。

exception optparse.OptionConflictError

当向 OptionParser 添加冲突的选项时引发。

exception optparse.OptionValueError

如果在命令行中遇到无效的选项值,则会引发此异常。

exception optparse.BadOptionError

如果在命令行上传递了无效选项,则会引发此异常。

exception optparse.AmbiguousOptionError

如果在命令行上传递了有歧义的选项,则会引发此异常。