argparse模組簡明教程
前言:閱讀原始碼時遇到argparse庫,我為了理解用法找了不少部落格,要麼晦澀難懂,要麼講的太淺。最後還是看了官方英文教程,講得非常清楚。本文大部分內容都來源於官方教程,我做了小小的翻譯和整理工作,為英文不太好的同學節省一下時間。這篇文章略長,但我相信只要你從頭讀到尾,一定可以很好地理解argparse的用法。
轉載請註明出處哦
1 argparse是幹什麼的
我們在寫python程式的時候,有時候希望在執行的時候附加一些引數。為了在程式中使用這些附加引數,我們需要正確地解析它們,這個時候argparse就派上用場了,argparse是一款簡單而又強大的命令列解析工具。
2 命令列引數的概念
首先,讓我們看看Linux中常用的ls
命令是怎麼為使用者提供服務的:
$ ls cpython devguide prog.py pypy rm-unused-function.patch $ ls pypy ctypes_configure demo dotviewer include lib_pypy lib-python ... $ ls -l total 20 drwxr-xr-x 19 wena wena 4096 Feb 18 18:51 cpython drwxr-xr-x 4 wena wena 4096 Feb 8 12:04 devguide -rwxr-xr-x 1 wena wena 535 Feb 19 00:05 prog.py drwxr-xr-x 14 wena wena 4096 Feb 7 00:59 pypy -rw-r--r-- 1 wena wena 741 Feb 18 01:01 rm-unused-function.patch $ ls --help Usage: ls [OPTION]... [FILE]... List information about the FILEs (the current directory by default). Sort entries alphabetically if none of -cftuvSUX nor --sort is specified. ...
從上面四條命令中我們可以學到這些東西:
ls
命令不加任何附加引數的時候就非常有用,它預設會展示當前資料夾裡的內容- 如果我們想讓它做預設之外的事,就得告訴它一些其他資訊。在這個例子裡,我們想展示當前資料夾下的pypy資料夾裡的內容,該怎麼做呢?實際上我們指定了一個位置引數 pypy,之所以叫位置引數,是因為僅僅憑藉它的位置資訊,解析器就知道該怎麼做。就比如
cp
命令,基礎的使用方法是cp SRC DEST
,第一個位置是想要複製的檔案,第二個位置是想要複製到的地方。 - 現在,我們想要改變程式的行為。在上面的例子中,我們想展示每個檔案的更多資訊,而不僅僅是檔名。
-l
做了這件事,它被稱為可選引數 - 指定--help會展示幫助文件,當遇到一個從來沒用過的程式時,幫助文件能夠讓你明白程式如何運作。
3 從一個簡單的例子開始
讓我們從一個簡單到幾乎什麼也不做的例子,來開始argparse的學習:
# prog.py
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
下面是我們從命令列執行程式碼的結果:
$ python3 prog.py
$ python3 prog.py --help
usage: prog.py [-h]
optional arguments:
-h, --help show this help message and exit
$ python3 prog.py --verbose
usage: prog.py [-h]
prog.py: error: unrecognized arguments: --verbose
$ python3 prog.py foo
usage: prog.py [-h]
prog.py: error: unrecognized arguments: foo
觀察上面的程式碼和執行過程,你能發現什麼呢?
- 當不加引數來執行指令碼時,什麼都不會列印到標準輸出上,程式看起來沒什麼用
- 第二個命令展現了argparse的用處,我們在prog.py腳本里幾乎什麼也沒做,但是我們已經擁有了一個幫助文件,只要指定
--help
就可以展示它 --help
引數也可以用更簡短的-h
來替代,它們是argparse唯一的免費引數,哈哈,之所以說是免費,是因為我們無需在程式中定義它們的行為就擁有了它們,觀察prog.py,並沒有任何和--help
相關的程式碼。但是,除了--help
,-h
之外,指定任何其他的附加引數都會報錯,因為我們沒有在程式中定義它們。接下來我會介紹如何定義自己的引數。
4 介紹位置引數
看這個例子:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo")
args = parser.parse_args()
print(args.echo)
命令列執行程式碼:
$ python3 prog.py
usage: prog.py [-h] echo
prog.py: error: the following arguments are required: echo
$ python3 prog.py --help
usage: prog.py [-h] echo
positional arguments:
echo
optional arguments:
-h, --help show this help message and exit
$ python3 prog.py foo
foo
觀察上面的程式碼和執行過程,你發現了什麼呢?
- 我們加了一個
add_argument
函式,這個函式允許我們指定自己的命令列引數。在這個例子中,我指定了一個名叫echo
的引數 - 現在我們執行程式會要求我們給出引數echo的值,不然就像第一行一樣報錯:
the following arguments are required: echo
parse_args()
會返回使用者給出的引數值,在這個例子中,是echo
引數的值。parse_args()
返回的引數的值可以通過.引數名
獲得,這個例子裡,我們通過args.echo
就可以得到echo
的引數值,是我們傳的字串'foo'。注意,這個引數名就是我們在add_argument
函式裡給出的字串'echo'。
看看上面--help
輸出的幫助文件,雖然看起來已經很不錯了,但是並不是很有幫助。比如,通過幫助文件,我們知道echo
是一個位置引數,但是並不知道它是什麼,除了看prog.py的原始碼我們根本不知道這個引數的作用是啥。所以,來讓它更有用吧:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("echo", help="echo the string you use here")
args = parser.parse_args()
print(args.echo)
然後我們就會看到:
$ python3 prog.py -h
usage: prog.py [-h] echo
positional arguments:
echo echo the string you use here
optional arguments:
-h, --help show this help message and exit
看出區別了嗎?現在來讓我們寫一些更加有用的程式吧!
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number")
args = parser.parse_args()
print(args.square**2)
上面的程式功能是從命令列讀取一個位置引數square,然後列印它的平方值。
看看執行過程:
$ python3 prog.py 4
Traceback (most recent call last):
File "prog.py", line 5, in <module>
print(args.square**2)
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
看起來程式沒有很好的運作呀。這是因為argparse預設將引數視為字串型別,除非我們顯式地告訴它這個引數是什麼型別。好,讓我們告訴argparse應該將輸入視作int型別:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", help="display a square of a given number",
type=int)
args = parser.parse_args()
print(args.square**2)
然後執行程式碼:
$ python3 prog.py 4
16
$ python3 prog.py four
usage: prog.py [-h] square
prog.py: error: argument square: invalid int value: 'four'
哇,很完美有沒有,程式現在可以很好地運作了。而且你發現沒,當我們輸入four作為引數時,這不是一個數字,如果不使用argparse,我們要到運算平方的時候才報型別錯誤,現在在引數解析的時候程式就會因為型別不匹配提前退出,很棒吧。
5 介紹可選引數
到目前為止我們一直在和位置引數打交道,讓我們看看怎麼新增可選引數吧!
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
if args.verbosity:
print("verbosity turned on")
執行程式:
$ python3 prog.py --verbosity 1
verbosity turned on
$ python3 prog.py
$ python3 prog.py --help
usage: prog.py [-h] [--verbosity VERBOSITY]
optional arguments:
-h, --help show this help message and exit
--verbosity VERBOSITY
increase output verbosity
$ python3 prog.py --verbosity
usage: prog.py [-h] [--verbosity VERBOSITY]
prog.py: error: argument --verbosity: expected one argument
觀察上面的程式碼和執行過程,你發現了什麼呢?
- 這個程式希望的功能是,當指定--verbosity輸出一些東西,反之什麼也不輸出
- 定義可選引數的方法是在引數名前面加兩道橫槓
--
- 為了證明可選引數真的是可選的,看上面的執行過程,當我們不指定--verbosity的時候程式沒有報錯,這就說明可選引數真的是可選的嗷。注意!當我們在程式中定義了可選引數,但是在執行的時候沒有用它,比如這個例子中的args.verbosity,那麼它的預設值是None,這也就是在不指定--verbosity時,if語句會False的原因。
上面的例子中,我們需要接受一個任意值給--verbosity。但實際上,我們希望指定--verbosity時,args.verbosity為True,不指定為False就夠了。能不能不指定一個值給--verbosity呢?看看這個例子
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
然後執行:
$ python3 prog.py --verbose
verbosity turned on
$ python3 prog.py --verbose 1
usage: prog.py [-h] [--verbose]
prog.py: error: unrecognized arguments: 1
$ python3 prog.py --help
usage: prog.py [-h] [--verbose]
optional arguments:
-h, --help show this help message and exit
--verbose increase output verbosity
觀察上面的程式碼和執行過程,發現了什麼?
- 現在這個--verbose選項更像是一個flag(標誌),而不是需要指定一個值的引數。我們用了一個新keyword,
action
,並且給了它一個值"store_true"
,這意味著,如果執行程式時指定了--verbose標誌,就把True
賦給args.verbose
,如果沒有指定,就把False
賦給它 - 現在,如果你還要給--verbose指定一個值,它就會報錯!
- 注意幫助文件的變化
簡短引數(short options)
如果你熟悉命令列的操作,你就會注意到,截至目前,我還沒有討論簡短引數。什麼是簡短引數呢?比如--help
,可以用-h
代替,一個橫槓開頭的-h
就是簡短引數。其實定義簡短引數非常簡單:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-v", "--verbose", help="increase output verbosity",
action="store_true")
args = parser.parse_args()
if args.verbose:
print("verbosity turned on")
然後就會這樣:
$ python3 prog.py -v
verbosity turned on
$ python3 prog.py --help
usage: prog.py [-h] [-v]
optional arguments:
-h, --help show this help message and exit
-v, --verbose increase output verbosity
注意幫助文件的變化。
6 結合使用位置引數和可選引數
來看一個更復雜的例子:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbose", action="store_true",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbose:
print("the square of {} equals {}".format(args.square, answer))
else:
print(answer)
執行程式碼:
$ python3 prog.py
usage: prog.py [-h] [-v] square
prog.py: error: the following arguments are required: square
$ python3 prog.py 4
16
$ python3 prog.py 4 --verbose
the square of 4 equals 16
$ python3 prog.py --verbose 4
the square of 4 equals 16
- 現在,我們定義了位置引數square,所以你不給它指定一個值程式就會報錯
- 注意,可選引數並不影響位置引數的位置,所以它們的順序無所謂。
好了,我們來試試讓--verbosity不再是flag,而是給它指定一個數字,如果數字越大,程式輸出的屁話(誤)越多。修改程式如下:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int,
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
看出區別了嗎,我們把--verbosity的type設成了int,並且把action='store_true'去掉了。這樣就要指定一個數字給它。
執行程式碼:
$ python3 prog.py 4
16
$ python3 prog.py 4 -v
usage: prog.py [-h] [-v VERBOSITY] square
prog.py: error: argument -v/--verbosity: expected one argument
$ python3 prog.py 4 -v 1
4^2 == 16
$ python3 prog.py 4 -v 2
the square of 4 equals 16
$ python3 prog.py 4 -v 3
16
看起來很不錯!除了最後一個,我們不希望賦3給verbosity時它反而輸出一個更短的句子,這是我們程式中的一個bug。讓我們來修復這個bug,將verbosity的可選值限制一下:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", type=int, choices=[0, 1, 2],
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
執行一下程式碼:
$ python3 prog.py 4 -v 3
usage: prog.py [-h] [-v {0,1,2}] square
prog.py: error: argument -v/--verbosity: invalid choice: 3 (choose from 0, 1, 2)
$ python3 prog.py 4 -h
usage: prog.py [-h] [-v {0,1,2}] square
positional arguments:
square display a square of a given number
optional arguments:
-h, --help show this help message and exit
-v {0,1,2}, --verbosity {0,1,2}
increase output verbosity
注意報錯資訊和幫助文件的變化。
好,接下來,讓我們來看看還能把這個verbosity玩出什麼花活,其實也是很常用的玩法,CPython直譯器的verbosity引數就是這種玩法,你可以執行python --help
驗證一哈。
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display the square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity == 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity == 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
這裡引入了另一個新的action,“count”,去計算選項被指定的次數。看下面的執行過程你就懂了:
$ python3 prog.py 4
16
$ python3 prog.py 4 -v
4^2 == 16
$ python3 prog.py 4 -vv
the square of 4 equals 16
$ python3 prog.py 4 --verbosity --verbosity
the square of 4 equals 16
$ python3 prog.py 4 -v 1
usage: prog.py [-h] [-v] square
prog.py: error: unrecognized arguments: 1
$ python3 prog.py 4 -h
usage: prog.py [-h] [-v] square
positional arguments:
square display a square of a given number
optional arguments:
-h, --help show this help message and exit
-v, --verbosity increase output verbosity
$ python3 prog.py 4 -vvv
16
- 現在-v/--verbosity看起來更像是一個flag(和
action='store_true'
有點類似),當你給--verbosity指定一個值就會報錯。 - 注意,如果你不指定-v或者--verbosity,args.verbosity的值不是0,而是None!這會導致Bug,待會說。
- 根據我們程式的需要,當指定三個以上的v,也應該和指定兩個v的輸出相同(兩個v就輸出了很多屁話了,說不出更多的屁話了)
- 有點不幸的是,幫助文件這次沒有表現出有用的資訊,它沒告訴我們可以多次指定v來獲得更詳盡的輸出。
- 最後一個執行結果也是我們程式的一個bug,指定了三個v卻得到很短的輸出。
讓我們來修復一下bug:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count",
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
# bugfix: replace == with >=
if args.verbosity >= 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity >= 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
然後執行:
$ python3 prog.py 4 -vvv
the square of 4 equals 16
$ python3 prog.py 4 -vvvv
the square of 4 equals 16
$ python3 prog.py 4
Traceback (most recent call last):
File "prog.py", line 11, in <module>
if args.verbosity >= 2:
TypeError: '>=' not supported between instances of 'NoneType' and 'int'
- 前兩個輸出很不錯,修復了我們之前的bug
- 第三個輸出存在問題。這就是我上面提到的,如果不指定-v,args.verbosity的值是None,而不是0!
讓我們修復第三個輸出的問題:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("square", type=int,
help="display a square of a given number")
parser.add_argument("-v", "--verbosity", action="count", default=0,
help="increase output verbosity")
args = parser.parse_args()
answer = args.square**2
if args.verbosity >= 2:
print("the square of {} equals {}".format(args.square, answer))
elif args.verbosity >= 1:
print("{}^2 == {}".format(args.square, answer))
else:
print(answer)
我們引入了一個新的keyword,default
,我們將它設為0,這樣在不指定-v時,args.verbosity被設為0,就可以和其他數字比較了!記住,如果某個可選引數在執行時沒有被指定,就會賦None值給它,而None是無法和int值做比較的!解決辦法就是給它一個defualt值。
現在:
$ python3 prog.py 4
16
總結
根據上面所學的知識,你已經可以做非常非常多的事了,但實際上我們也只涉及到argparse強大功能的表面而已。argparse非常強大,如果有需要,大夥可以查閱argparse的官方api文件。
碼字不易,如果覺得文章不錯的話,點個贊再走吧,秋梨膏!