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

如果在命令列上傳遞了有歧義的選項,則會引發此異常。