optparse
— 命令行选项解析器¶
源代码: Lib/optparse.py
选择参数解析库¶
标准库包含三个参数解析库
getopt
: 一个紧密模仿 C 语言过程式getopt
API 的模块。自 Python 1.0 初始版本发布前就已包含在标准库中。optparse
:getopt
的一个声明式替代品,提供等效功能,而无需每个应用程序实现自己的过程式选项解析逻辑。自 Python 2.3 版本起包含在标准库中。argparse
:optparse
的一个更具主见(opinionated)的替代方案,默认提供更多功能,但代价是应用程序在精确控制参数处理方式方面的灵活性降低。自 Python 2.7 和 Python 3.2 版本起包含在标准库中。
在没有更具体的参数解析设计约束的情况下,推荐使用 argparse
来实现命令行应用程序,因为它能以最少的应用程序级代码提供最高水平的基线功能。
getopt
的保留几乎完全是出于向后兼容性的原因。然而,它也服务于一个特定的用例,即作为在基于 getopt
的 C 应用程序中原型化和测试命令行参数处理的工具。
在以下情况下,应考虑将 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
),它允许将命令行应用程序开发为一组经过装饰的命令实现函数。
其他第三方库,如 typer 或 msgspec-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
将是 False
。optparse
支持长选项和短选项,允许将短选项合并在一起,并允许选项以多种方式与其参数关联。因此,以下命令行都等同于上面的示例:
<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
就是一个选项参数。foo
和 bar
是位置参数。
选项的用途是什么?¶
选项用于提供额外信息以调整或定制程序的执行。以防不清楚,选项通常是*可选的*。一个程序应该能够在没有任何选项的情况下正常运行。(从 Unix 或 GNU 工具集中随机挑选一个程序。它能在没有任何选项的情况下运行并仍然有意义吗?主要的例外是 find
、tar
和 dd
——所有这些都是因其非标准语法和令人困惑的界面而受到合理批评的奇特异类。)
很多人希望他们的程序有“必需的选项”。想一想。如果它是必需的,那它就*不是可选的*!如果你的程序为了成功运行绝对需要某条信息,那正是位置参数的用途。
作为良好命令行界面设计的一个例子,考虑一下不起眼的 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
,解析选项后剩下的位置参数列表。
本教程部分仅涵盖四个最重要的选项属性:action
、type
、dest
(目标)和 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
支持的其他一些选项类型是 int
和 float
。这里有一个期望整数参数的选项:
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_true
和 store_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
允许你为每个目标提供一个默认值,该值在解析命令行之前被赋值。
首先,考虑详细/安静的例子。如果我们希望 optparse
将 verbose
设置为 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()
方法向组中添加选项。
一旦所有选项都声明完毕,就可以使用 OptionParser
的 add_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.OptionError
或 TypeError
),让程序崩溃。
处理用户错误要重要得多,因为无论你的代码多稳定,这些错误都保证会发生。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
在展开
usage
和version
中的%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
实例的规范方式是使用 OptionParser
的 add_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,你还可以提供type
和dest
选项属性;请参阅 标准选项动作。)
如你所见,大多数 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"
选项属性¶
- class optparse.Option¶
单个命令行参数,其各种属性通过关键字参数传递给构造函数。通常通过
OptionParser.add_option()
创建而不是直接创建,并且可以通过传递给OptionParser
的 option_class 参数的自定义类来覆盖。
以下选项属性可以作为关键字参数传递给 OptionParser.add_option()
。如果你传递了一个与特定选项无关的选项属性,或者未能传递一个必需的选项属性,optparse
会引发 OptionError
。
- Option.dest¶
(默认值:从选项字符串派生)
如果选项的动作意味着在某处写入或修改一个值,这个属性会告诉
optparse
写入的位置:dest
指定了options
对象的一个属性,optparse
在解析命令行时会构建这个对象。
- Option.default¶
如果在命令行中没有看到该选项,则用于此选项目标的值。另请参阅
OptionParser.set_defaults()
。
- Option.const¶
对于存储常量值的动作,这是要存储的常量值。
- Option.choices¶
对于类型为
"choice"
的选项,这是用户可以选择的字符串列表。
标准选项动作¶
各种选项动作都有略微不同的要求和效果。大多数动作有几个相关的选项属性,你可以指定它们来指导 optparse
的行为;少数动作有必需的属性,你必须为任何使用该动作的选项指定这些属性。
"store"
[相关:type
,dest
,nargs
,choices
]该选项后必须跟一个参数,该参数会根据
type
转换为一个值并存储在dest
中。如果nargs
> 1,将从命令行使用多个参数;所有参数都将根据type
进行转换,并以元组形式存储到dest
。请参阅 标准选项类型 部分。如果提供了
choices
(一个字符串列表或元组),类型默认为"choice"
。如果未提供
type
,则默认为"string"
。如果未提供
dest
,optparse
会从第一个长选项字符串派生一个目标(例如,--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
]示例
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")
如果看到
--noisy
,optparse
将设置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"
[相关:type
、dest
、nargs
、choices
]该选项后必须跟一个参数,该参数会附加到
dest
中的列表中。如果未提供dest
的默认值,当optparse
首次在命令行上遇到此选项时,会自动创建一个空列表。如果nargs
> 1,则会使用多个参数,并且一个长度为nargs
的元组将被附加到dest
。type
和dest
的默认值与"store"
动作相同。示例
parser.add_option("-t", "--tracks", action="append", type="int")
如果在命令行中看到
-t3
,optparse
会执行以下等效操作: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
;相关:type
、nargs
、callback_args
、callback_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_args
和 callback_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
的类型将是选项类型所隐含的类型。如果此选项的type
是None
(不期望参数),那么value
将是None
。如果nargs
> 1,value
将是一个包含适当类型值的元组。解析器
是驱动整个过程的 OptionParser 实例,主要有用,因为你可以通过其实例属性访问一些其他有趣的数据:
parser.largs
当前剩余参数的列表,即那些已被处理但既非选项也非选项参数的参数。你可以随意修改
parser.largs
,例如向其中添加更多参数。(这个列表将成为parse_args()
的第二个返回值args
。)parser.rargs
当前剩余参数的列表,即移除了
opt_str
和value
(如果适用)之后,只剩下它们后面的参数。你可以随意修改parser.rargs
,例如通过消耗更多参数。parser.values
默认存储选项值的对象(optparse.OptionValues 的实例)。这让回调函数可以使用与
optparse
其余部分相同的机制来存储选项值;你不需要处理全局变量或闭包。你还可以访问或修改在命令行上已遇到的任何选项的值。
args
是通过
callback_args
选项属性提供的任意位置参数的元组。kwargs
是通过
callback_kwargs
提供的任意关键字参数的字典。
在回调中引发错误¶
如果选项或其参数有任何问题,回调函数应该引发 OptionValueError
。optparse
会捕获这个异常并终止程序,将你提供的错误消息打印到标准错误输出。你的消息应该清晰、简洁、准确,并提及出错的选项。否则,用户将很难弄清楚他们做错了什么。
回调示例 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
解释命令行选项的两个主要控制因素是每个选项的动作和类型,所以最可能的扩展方向是添加新的动作和新的类型。
添加新类型¶
要添加新类型,你需要定义自己的 optparse
的 Option
类的子类。这个类有几个属性定义了 optparse
的类型:TYPES
和 TYPE_CHECKER
。
- Option.TYPE_CHECKER¶
一个将类型名称映射到类型检查函数的字典。类型检查函数具有以下签名:
def check_mytype(option, opt, value)
其中
option
是一个Option
实例,opt
是一个选项字符串(例如-f
),而value
是来自命令行的字符串,需要被检查并转换为你期望的类型。check_mytype()
应该返回一个假设类型为mytype
的对象。类型检查函数返回的值最终会出现在OptionParser.parse_args()
返回的 OptionValues 实例中,或者作为value
参数传递给回调函数。如果遇到任何问题,你的类型检查函数应该引发
OptionValueError
。OptionValueError
接受一个字符串参数,该参数会原封不动地传递给OptionParser
的error()
方法,该方法会依次在前面加上程序名称和字符串"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_CHECKER
的 copy()
,我们最终会修改 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_ACTIONS
和TYPED_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)
如果
values
的attr
属性不存在或者是None
,那么 ensure_value() 会先将其设置为value
,然后返回value
。这对于像"extend"
、"append"
和"count"
这样的 action 非常方便,它们都在一个变量中累积数据,并期望该变量是某种特定类型(前两者是列表,后者是整数)。使用ensure_value()
意味着使用你的 action 的脚本不必担心为相关选项目标设置默认值;它们可以简单地将默认值保留为None
,而ensure_value()
会在需要时负责将其设置正确。
异常¶
- exception optparse.OptionConflictError¶
当向
OptionParser
添加冲突的选项时引发。
- exception optparse.OptionValueError¶
如果在命令行中遇到无效的选项值,则会引发此异常。
- exception optparse.BadOptionError¶
如果在命令行上传递了无效选项,则会引发此异常。
- exception optparse.AmbiguousOptionError¶
如果在命令行上传递了有歧义的选项,则会引发此异常。