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¶
如果在命令列上傳遞了有歧義的選項,則會引發此異常。