謹記四條規則,便可寫出完美的Python命令行程序

2020-12-10 電子產品世界

  作為 Python 開發者,我們經常要編寫命令行程序。比如在我的數據科學項目中,我要從命令行運行腳本來訓練模型,以及計算算法的準確率等。

本文引用地址:http://www.eepw.com.cn/article/201901/397090.htm

  因此,更方便更易用的腳本能夠很好地提高生產力,特別是在有多個開發者從事同一個項目的場合下。

  因此,我建議你遵循以下四條規則:

  儘可能提供默認參數值所有錯誤情況必須處理(例如,參數缺失,類型錯誤,找不到文件)所有參數和選項必須有文檔不是立即完成的任務應當顯示進度條

  舉個簡單的例子

  我們把這些規則應用到一個具體的例子上。這個腳本可以使用凱撒加密法加密和解密消息。

  假設已經有個寫好的 encrypt 函數(實現如下),我們需要創建一個簡單的腳本,用來加密和解密消息。我們希望讓用戶通過命令行參數選擇加密模式(默認)和解密模式,並選擇一個秘鑰(默認為 1)。

  defencrypt(plaintext, key): cyphertext = ''for character in plaintext:if character.isalpha(): number = ord(character) number += keyif character.isupper():if number > ord('Z'): number -= 26elif number < ord('A'):number += 26elif character.islower():if number > ord('z'): number -= 26elif number < ord('a'): number += 26 character = chr(number) cyphertext += characterreturn cyphertext

  我們的腳本需要做的第一件事就是獲取命令行參數的值。當我搜索「python command line arguments」時,出現的第一個結果是關於sys.argv的,所以我們來試試這個方法……

  「初學者」的方法

  sys.argv 是個列表,包含用戶在運行腳本時輸入的所有參數(包括腳本名自身)。

  例如,如果我輸入:

  > pythoncaesar_script.py--key 23 --decryptmysecretmessagepbvhfuhwphvvdjh

  該列表將包含:

  ['caesar_script.py', '--key', '23', '--decrypt', 'my', 'secret', 'message']

  因此只需遍歷該參數列表,找到'--key'(或'-k')以得到秘鑰值,找到'--decrypt'以設置解密模式(實際上只需要使用秘鑰的反轉作為秘鑰即可)。

  最後我們的腳本大致如下:

  import sysfrom caesar_encryption import encryptdefcaesar(): key = 1is_error = Falsefor index, arg in enumerate(sys.argv):if arg in ['--key', '-k'] and len(sys.argv) > index + 1: key = int(sys.argv[index + 1])del sys.argv[index]del sys.argv[index]breakfor index, arg in enumerate(sys.argv):if arg in ['--encrypt', '-e']:del sys.argv[index]breakif arg in ['--decrypt', '-d']: key = -keydel sys.argv[index]breakif len(sys.argv) == 1: is_error = Trueelse:for arg in sys.argv:if arg.startswith('-'): is_error = Trueif is_error: print(f'Usage: python {sys.argv[0]} [ --key] [ --encrypt|decrypt ]')else: print(encrypt(' '.join(sys.argv[1:]), key))if __name__ == '__main__': caesar()

  這個腳本遵循了一些我們前面推薦的規則:

  支持默認秘鑰和默認模式基本的錯誤處理(沒有提供輸入文本的情況,以及提供了無法識別的參數的情況)出錯時或者不帶任何參數調用腳本時會顯示文檔:> pythoncaesar_script_using_sys_argv.pyUsage: pythoncaesar.py[ --key][ --encrypt|decrypt ]

  但是,這個凱撒加密法腳本太長了(39 行,其中甚至還沒包括加密代碼本身),而且很難讀懂。

  解析命令行參數應該還有更好的辦法……

  試試 argparse?

  argparse 是 Python 用來解析命令行參數的標準庫。

  我們來看看用 argparse 怎樣編寫凱撒加密的腳本:

  import argparsefrom caesar_encryption import encryptdef caesar(): parser = argparse.ArgumentParser()group = parser.add_mutually_exclusive_group()group.add_argument('-e', '--encrypt', action='store_true')group.add_argument('-d', '--decrypt', action='store_true')parser.add_argument('text', nargs='*') parser.add_argument('-k', '--key', type=int, default=1) args = parser.parse_args() text_string = ' '.join(args.text)key = args.keyif args.decrypt: key = -key cyphertext = encrypt(text_string, key) print(cyphertext)if __name__ == '__main__': caesar()

  這段代碼也遵循了上述規則,而且與前面的手工編寫的腳本相比,可以提供更準確的文檔,以及更具有交互性的錯誤處理:

  > pythoncaesar_script_using_argparse.py--encodeMymessageusage: caesar_script_using_argparse.py[-h][-e | -d][-k KEY][text [text ...]]caesar_script_using_argparse.py: error: unrecognizedarguments: --encode> pythoncaesar_script_using_argparse.py--helpusage: caesar_script_using_argparse.py[-h][-e | -d][-k KEY][text [text ...]]

  positional arguments:textoptional arguments: -h, --help show this help message andexit -e, --encrypt -d, --decrypt -k KEY, --keyKEY

  但是,仔細看了這段代碼後,我發現(雖然有點主觀)函數開頭的幾行(從7行到13行)定義了參數,但定義方式並不太優雅:它太臃腫了,而且完全是程式化的。應該有更描述性、更簡潔的方法。

  click 能做得更好!

  幸運的是,有個 Python 庫能提供與 argparse 同樣的功能(甚至還能提供更多),它的代碼風格更優雅。這個庫的名字叫 click。

  這裡是凱撒加密腳本的第三版,使用了 click:

  import clickfrom caesar_encryption import encrypt@click.command()@click.argument('text', nargs=-1)@click.option('--decrypt/--encrypt', '-d/-e')@click.option('--key', '-k', default=1)def caesar(text, decrypt, key): text_string = ' '.join(text)if decrypt: key = -key cyphertext = encrypt(text_string, key) click.echo(cyphertext)if __name__ == '__main__':caesar()

  注意現在參數和選項都在修飾器裡定義,定義好的參數直接作為函數參數提供。

  我來解釋一下上面代碼中的一些地方:

  腳本參數定義中的nargs參數指定了該參數期待的單詞的數目(一個用引號括起來的字符串算一個單詞)。默認值是1。這裡nargs=-1允許接收任意數目的單詞。--encrypt/--decrypt這種寫法可以定義完全互斥的選項(類似於argparse中的add_mutually_exclusive_group函數),它將產生一個布爾型參數。click.echo是該庫提供的一個工具函數,它的功能與print相同,但兼容Python 2和Python 3,還有一些其他功能(如處理顏色等)。

  添加一些隱秘性

  這個腳本的參數(被加密的消息)應當是最高機密。而我們卻要求用戶直接在終端裡輸入文本,使得這些文本被記錄在命令歷史中,這不是很諷刺嗎?

  解決方法之一就是使用隱藏的提示。或者可以從輸入文件中讀取文本,對於較長的文本來說更實際一些。或者可以乾脆讓用戶選擇。

  輸出也一樣:用戶可以保存到文件中,也可以輸出到終端。這樣就得到了凱撒腳本的最後一個版本:

  import clickfrom caesar_encryption import encrypt@click.command()@click.option('--input_file', type=click.File('r'),help='File in which there is the text you want to encrypt/decrypt.''If not provided, a prompt will allow you to type the input text.',)@click.option('--output_file', type=click.File('w'), help='File in which the encrypted / decrypted text will be written.''If not provided, the output text will just be printed.',)@click.option('--decrypt/--encrypt','-d/-e', help='Whether you want to encrypt the input text or decrypt it.')@click.option('--key','-k',default=1,help='The numeric key to use for the caesar encryption / decryption.')def caesar(input_file, output_file, decrypt, key):if input_file:text = input_file.read()else:text = click.prompt('Enter a text', hide_input=not decrypt)if decrypt:key = -key cyphertext = encrypt(text, key)if output_file:output_file.write(cyphertext)else: click.echo(cyphertext)if __name__ == '__main__': caesar()

  這個版本有什麼新東西嗎?

  首先,注意到我給每個參數選項都加了個help參數。由於腳本變得複雜了,help參數可以給腳本的行為添加一些文檔。運行結果如下:> python caesar_script_v2.py --helpUsage: caesar_script_v2.py [OPTIONS]Options: --input_file FILENAME File in which there is the text you want to encrypt/decrypt. Ifnot provided, a prompt will allow you to type the input text. --output_file FILENAME File in which the encrypted/decrypted text will be written. Ifnot provided, the output text will just be printed. -d, --decrypt / -e, --encrypt Whether you want to encrypt the input textor decrypt it.-k, --keyINTEGER The numeric keyto use for the caesar encryption / decryption. --help Show this message andexit.

  兩個新的參數:input_file 和 output_file,類型均為 click.File。該庫能夠用正確的模式打開文件,處理可能的錯誤,再執行函數。例如:> python caesar_script_v2.py --decrypt --input_file wrong_file.txtUsage: caesar_script_v2.py [OPTIONS]Error: Invalid value for"--input_file": Could notopen file: wrong_file.txt: No such file or directory

  正像help文本中解釋的那樣,如果沒有提供input_file,就使用click.promp讓用戶直接在提示符下輸入文本,在加密模式下這些文本是隱藏的。如下所示:> python caesar_script_v2.py --encrypt --key 2Enter a text: **************yyy.ukectc.eqo

  破解密文!

  現在設想你是個黑客:你要解密一個用凱撒加密過的密文,但你不知道秘鑰是什麼。

  最簡單的策略就是用所有可能的秘鑰調用解密函數 25 次,閱讀解密結果,看看哪個是合理的。

  但你很聰明,而且也很懶,所以你想讓整個過程自動化。確定解密後的 25 個文本哪個最可能是原始文本的方法之一,就是統計所有這些文本中的英文單詞的個數。這可以使用 PyEnchant 模塊實現:

  import clickimport enchantfrom caesar_encryption import encrypt@click.command()@click.option('--input_file', type=click.File('r'),required=True,)@click.option('--output_file', type=click.File('w'),required=True,)defcaesar_breaker(input_file, output_file): cyphertext = input_file.read() english_dictionnary = enchant.Dict("en_US")max_number_of_english_words = 0for key in range(26): plaintext = encrypt(cyphertext, -key) number_of_english_words = 0for word in plaintext.split(' '):if word and english_dictionnary.check(word):number_of_english_words += 1if number_of_english_words > max_number_of_english_words: max_number_of_english_words = number_of_english_words best_plaintext = plaintext best_key = keyclick.echo(f'The most likely encryption key is {best_key}. It gives the following plaintext:\n\n{best_plaintext[:1000]}...')output_file.write(best_plaintext)if __name__ == '__main__':caesar_breaker()

  


  貌似運行得很不錯,但別忘了,好的命令行程序還有個規則需要遵守:

  4.A 不是立即完成的任務應當顯示進度條。

  示例中的文本包含10^4個單詞,因此該腳本需要大約5秒才能解密。這很正常,因為它需要檢查所有25個秘鑰,每個秘鑰都要檢查10^4個單詞是否出現在英文字典中。

  假設你要解密的文本包括10^5個但IC,那麼就要花費50秒才能輸出結果,用戶可能會非常著急。

  因此我建議這種任務一定要顯示進度條。特別是,顯示進度條還非常容易實現。

  下面是個顯示進度條的例子:

  import clickimport enchantfrom tqdm import tqdmfrom caesar_encryption import encrypt@click.command()@click.option('--input_file',type=click.File('r'), required=True,)@click.option('--output_file',type=click.File('w'), required=True,)defcaesar_breaker(input_file, output_file): cyphertext = input_file.read() english_dictionnary = enchant.Dict("en_US") best_number_of_english_words = 0for key in tqdm(range(26)): plaintext = encrypt(cyphertext, -key)number_of_english_words = 0for word in plaintext.split(' '):if word and english_dictionnary.check(word): number_of_english_words += 1if number_of_english_words > best_number_of_english_words:best_number_of_english_words = number_of_english_words best_plaintext = plaintext best_key = key click.echo(f'The most likely encryption key is {best_key}. It gives the following plaintext:\n\n{best_plaintext[:1000]}...')output_file.write(best_plaintext)if __name__ == '__main__':caesar_breaker()

  你發現區別了嗎?可能不太好找,因為區別真的很小,只有四個字母:tqdm。

  tqdm 是 Python 庫的名字,也是它包含的類的名字。只需用它包裹一個可迭代的東西,就能顯示出進度條:

  forkeyin tqdm(range(26)):

  這樣就能顯示出非常漂亮的進度條。我都不敢相信這是真的。

  


  另外,click也提供類似的顯示進度條的工具(click.progress_bar),但我覺得它的外觀不太容易懂,而且要寫的代碼也多一些。

  我希望這篇文章能讓你在改進開發者的體驗上多花點時間。

相關焦點

  • 深入淺出 + 徹底理解 Python 編碼
    本文使用的編程環境是centos6.7,python2.7。我們在shell中鍵入python以打開python命令行,並鍵入如下兩句話:s = "中國zg" e = s.encode("utf-8")現在的問題是:這段代碼能運行嗎?
  • nodejs實現命令行工具
    為什麼使用nodejs實現命令行工具Node.js是一個基於事件驅動I/O的JavaScript環境,基於Google的V8引擎,V8引擎執行Javascript的速度非常快,性能非常好。眾所周知,javascript已經成為最流行的程式語言,以前javascript只是用來實現web前端功能,但是nodejs的出現使得javascript不僅可以服務於web的後端,還能夠作為通用的腳本語言,類似python/perl一樣,實現更多的桌面相關的任務。
  • Python編程:開發工具(IDE)大匯總(附官方下載)
    最近有多位讀者留言,諮詢更便捷、高效的python編程開發工具(IDE),本文就給大家介紹四款業內常用的python軟體開發工具,並簡單介紹下每款軟體的特點,便於讀者按需選擇合適的學習軟體!pip安裝器一鍵全搞定以管理員方式打開命令行,輸入:pip install jupyter 等待安裝完成在python工程文件夾下打開命令行窗口,輸入:jupyter notebook 回車即可jupyter notebook啟動後會在默認瀏覽器打開網頁,創建工程與代碼編寫和運行都在網頁上完成jupyter notebook打開界面如下:
  • MicroPython:TPYBoard 開發板如何運行第一個腳本?
    通過 USB 線連接你的 PC 機(windows,mac,linux皆可)。你不可能搞錯因為僅有這麼一種連接方式。當連接成功後開發板將上電和進入開機程序,綠色的 LED 燈應該在半秒或更少的時間內亮起,當其熄滅時意味著開機程序已完成。安裝USB驅動Windows:開發板將作為可移動磁碟出現。
  • python運算符總結
    程序語言的特性總是為解決生活中遇到的實際問題而設計的。脫離生活實質的無根源的設計是為意義的。正如運算符,正是因為我們在生活中,總是遇到各種各校的運算問題。python算術運算符加減乘除是最基本的數學規則,python中當然不能忽視。我們通過交互式解釋器來演示。在cmd命令行下,輸入python回車,即可打開交互式窗口。
  • python中函數的運用(1)
    有的人說,為何每次都是a,b,或者x,y行不行,也都可以,你定義一個xx 和yy都是可以的。 其實我們就是先定義一個加法計算的通用公式,比如a+b,這就是加法描述的一般形式,然後,這裡面需要2個未知數, 那麼我們傳入2個數據就可以了。
  • Python常用庫大全
    python-decouple – 將設置和代碼完全隔離。 命令行工具用於創建命令行程序的庫。命令行程序開發 cement – Python 的命令行程序框架。 click – 一個通過組合的方式來創建精美命令行界面的包。 cliff – 一個用於創建命令行程序的框架,可以創建具有多層命令的命令行程序。 clint – Python 命令行程序工具。 colorama – 跨平臺彩色終端文本。
  • Python 實現蘿拉遊戲(四連環、重力四子棋)
    遊戲規則:雙方輪流選擇棋盤的列號放進自己的棋子,若棋盤上有四顆相同型號的棋子在一行、一列或一條斜線上連接起來,則使用該型號棋子的玩家就贏了!程序實現遊戲,並將每局的數據保存到本地的文件中首先我們要創建一個空白的棋盤```pythondef into():#初始空白棋盤for i in range(6):list_width=[]for j in range(8):list_width.append(' '+'|')
  • Python學習第137課——正則表達式中實現可選規則
    【每天幾分鐘,從零入門python編程的世界!】正則表達式英文是Regular Expression,各種程式語言中都是把它簡寫成Regex,或者Regexp或者re。這節我們學習如何在正則表達式中實現可選規則。
  • 35行代碼利用python生成字符畫,非常適合初學者練習,附源碼!
    35行代碼利用python生成字符畫,非常適合初學者練習,附源碼! python字符畫生成的原理及其簡單,一句話概括就是----將圖片像素點用不同字符代替,從而將像素組成的圖片轉變成用字符組成的字符畫。為了儘可能的使得字符畫展示效果與圖片相同。
  • Python學習第164課——Linux命令行特殊符號的意義以及命令的語法...
    這節我們介紹Linux系統命令行中的一些特定的符號具有什麼含義,以及命令行的語法規則。●Linux命令行中特定的符號的含義比如我登錄到系統中之後,會顯示[xiaozhi@localhost~]$這一行符號,xiaozhi就是我登錄到系統後正在使用這臺電腦的用戶名,你的命令行顯示的就是你自己登錄到系統中的用戶名
  • 深度學習教程2,Anaconda安裝和使用命令
    python管理器,可以安裝python各種版本並存,可以安裝各種庫的各種版本並存。可以很方便的使用在比如別人特定版本庫開發的程序的環境。官網:https://www.anaconda.com/下載地址:win版:Anaconda3-5.3.1-Windows-x86_64.exe 633M按圖片設置,然後都是下一步就可以完成安裝啦。
  • 菜鳥學Python 安裝教程和常見問題
    一、什麼是 pythonpython 是一門解釋性語言,語法簡單,有大量的擴展包,如處理圖像,爬取網頁等等。(需安裝 python 的解釋器,有了解釋器,即可開始進行 python 編程。)windows 版,mac 版,Linux 版)x86_64 表示是 64 位系統x_86 表示是 32 位系統Mac 版安裝時:尾綴 Pkg 是可視化安裝包,類似於 windows 風格的一直點下一步那種安裝方式尾綴 Sh 是命令行方式安裝
  • 從一個簡單的程序開始學習Python編程
    然後筆者發現,自己能為小白貢獻一些力量,然後決定創作適合小白的python編程類內容。今天,筆者想帶編程小白們從一個簡單的程序開始學習Python編程。在前一篇文章>分別實現顯示文字"貨幣兌換程序1.0",及把1.6314賦值給rate變量。
  • 新手寫Python程序有什麼推薦好用編輯器
    安裝完成python,需要一個稱手的編輯器。俗話說:工欲善其事,必先利其器。那到底新手寫Python程序有什麼推薦好用編輯器,網上一大堆編輯器不可能每一個都試一下。現在給點我自己學習python過程中選擇編輯器一些個人看法。
  • python生成字符串
    大家好,我是酷叮貓少兒編程的隋老師,今天給大家介紹的是一個非常有意思的小程序。相信大家都看過很多非常漂亮的圖片,那麼python如何把圖片用字符顯示出來呢?那接下來就和老師一起來編寫這個程序吧。一、首先,讓我們安裝一個非常著名的圖形庫名字叫pillow如果各位同學的電腦上已經安裝了python環境,那麼執行下面的指令python -m pip install pillow安裝完畢,我們就可以使用pillow 庫來繪製圖形了。
  • 慢步學習,python編程實例中,對遍歷程序結構for語句的解析
    第1-5行,三引號內為注釋,供程式設計師閱讀。第6行為引用python-docx庫內的Document模塊(python-docx庫的引用默認為docx,和庫設計有關,其他庫一般用庫名稱)。第8行,打開word文檔(123.docx),用變量d來指代打開的文檔,後面代碼中 d 就是我們這裡打開的123.docx。第9行,獲得文檔段落數目。在Document模塊內,文檔對象d,有一個paragraphs屬性,d.paragraphs是文檔d的所有段落。
  • Linux文件的常用操作命令
    reload # 重載reboot # 重啟halt # 關機poweroff # 關機2、查看文件常用命令cat # 在命令提示符下查看文件內容more # 在命令提示符中分頁查看文件內容less # 命令行中查看文件可以上下翻頁反覆瀏覽head # 命令行中查看文件頭幾行tail # 命令行中查看文件尾幾行wc # 統計文件的單詞數 行數等信息3、目錄管理常用指令pwd #
  • 零基礎快速入門python教程,結合新手練習的5大項目
    如何運行Python通常來講有二種方式,一種方式是交互式的,就像Shell命令行提示符那樣,交互式的,輸入,就有輸出;在終端輸入python命令,就進入了Python的命令提示符中:>>>輸入Python語句,解釋器就會執行,並輸出結果,如:
  • Windows Server安裝tensorflow錯誤完美解決
    命令行下運行import tensorflow時,會出現如下錯誤:Traceback (most recent call last): File 「D:\Python35\lib\site-packages\tensorflow\python\pywrap_tensorflow.py」, line