<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 設(shè)計(jì)應(yīng)用 > 謹(jǐn)記四條規(guī)則,便可寫(xiě)出完美的Python命令行程序

          謹(jǐn)記四條規(guī)則,便可寫(xiě)出完美的Python命令行程序

          作者: 時(shí)間:2019-01-24 來(lái)源:網(wǎng)絡(luò) 收藏

            作為 開(kāi)發(fā)者,我們經(jīng)常要編寫(xiě)命令行程序。比如在我的數(shù)據(jù)科學(xué)項(xiàng)目中,我要從命令行運(yùn)行腳本來(lái)訓(xùn)練模型,以及計(jì)算算法的準(zhǔn)確率等。

          本文引用地址:http://www.ex-cimer.com/article/201901/397090.htm

            因此,更方便更易用的腳本能夠很好地提高生產(chǎn)力,特別是在有多個(gè)開(kāi)發(fā)者從事同一個(gè)項(xiàng)目的場(chǎng)合下。

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

            盡可能提供默認(rèn)參數(shù)值所有錯(cuò)誤情況必須處理(例如,參數(shù)缺失,類(lèi)型錯(cuò)誤,找不到文件)所有參數(shù)和選項(xiàng)必須有文檔不是立即完成的任務(wù)應(yīng)當(dāng)顯示進(jìn)度條

            舉個(gè)簡(jiǎn)單的例子

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

            假設(shè)已經(jīng)有個(gè)寫(xiě)好的 encrypt 函數(shù)(實(shí)現(xiàn)如下),我們需要?jiǎng)?chuàng)建一個(gè)簡(jiǎn)單的腳本,用來(lái)加密和解密消息。我們希望讓用戶通過(guò)命令行參數(shù)選擇加密模式(默認(rèn))和解密模式,并選擇一個(gè)秘鑰(默認(rèn)為 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

            我們的腳本需要做的第一件事就是獲取命令行參數(shù)的值。當(dāng)我搜索“python command line arguments”時(shí),出現(xiàn)的第一個(gè)結(jié)果是關(guān)于sys.argv的,所以我們來(lái)試試這個(gè)方法……

            “初學(xué)者”的方法

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

            例如,如果我輸入:

            > pythoncaesar_script.py--key 23 --decryptmysecretmessagepbvhfuhwphvvdjh

            該列表將包含:

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

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

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

            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()

            這個(gè)腳本遵循了一些我們前面推薦的規(guī)則:

            支持默認(rèn)秘鑰和默認(rèn)模式基本的錯(cuò)誤處理(沒(méi)有提供輸入文本的情況,以及提供了無(wú)法識(shí)別的參數(shù)的情況)出錯(cuò)時(shí)或者不帶任何參數(shù)調(diào)用腳本時(shí)會(huì)顯示文檔:> pythoncaesar_script_using_sys_argv.pyUsage: pythoncaesar.py[ --key][ --encrypt|decrypt ]

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

            解析命令行參數(shù)應(yīng)該還有更好的辦法……

            試試 argparse?

            argparse 是 用來(lái)解析命令行參數(shù)的標(biāo)準(zhǔn)庫(kù)。

            我們來(lái)看看用 argparse 怎樣編寫(xiě)凱撒加密的腳本:

            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()

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

            > 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

            但是,仔細(xì)看了這段代碼后,我發(fā)現(xiàn)(雖然有點(diǎn)主觀)函數(shù)開(kāi)頭的幾行(從7行到13行)定義了參數(shù),但定義方式并不太優(yōu)雅:它太臃腫了,而且完全是程式化的。應(yīng)該有更描述性、更簡(jiǎn)潔的方法。

            click 能做得更好!

            幸運(yùn)的是,有個(gè) 庫(kù)能提供與 argparse 同樣的功能(甚至還能提供更多),它的代碼風(fēng)格更優(yōu)雅。這個(gè)庫(kù)的名字叫 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()

            注意現(xiàn)在參數(shù)和選項(xiàng)都在修飾器里定義,定義好的參數(shù)直接作為函數(shù)參數(shù)提供。

            我來(lái)解釋一下上面代碼中的一些地方:

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

            添加一些隱秘性

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

            解決方法之一就是使用隱藏的提示?;蛘呖梢詮妮斎胛募凶x取文本,對(duì)于較長(zhǎng)的文本來(lái)說(shuō)更實(shí)際一些?;蛘呖梢愿纱嘧層脩暨x擇。

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

            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()

            這個(gè)版本有什么新東西嗎?

            首先,注意到我給每個(gè)參數(shù)選項(xiàng)都加了個(gè)help參數(shù)。由于腳本變得復(fù)雜了,help參數(shù)可以給腳本的行為添加一些文檔。運(yùn)行結(jié)果如下:> 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.

            兩個(gè)新的參數(shù):input_file 和 output_file,類(lèi)型均為 click.File。該庫(kù)能夠用正確的模式打開(kāi)文件,處理可能的錯(cuò)誤,再執(zhí)行函數(shù)。例如:> 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文本中解釋的那樣,如果沒(méi)有提供input_file,就使用click.promp讓用戶直接在提示符下輸入文本,在加密模式下這些文本是隱藏的。如下所示:> python caesar_script_v2.py --encrypt --key 2Enter a text: **************yyy.ukectc.eqo

            破解密文!

            現(xiàn)在設(shè)想你是個(gè)黑客:你要解密一個(gè)用凱撒加密過(guò)的密文,但你不知道秘鑰是什么。

            最簡(jiǎn)單的策略就是用所有可能的秘鑰調(diào)用解密函數(shù) 25 次,閱讀解密結(jié)果,看看哪個(gè)是合理的。

            但你很聰明,而且也很懶,所以你想讓整個(gè)過(guò)程自動(dòng)化。確定解密后的 25 個(gè)文本哪個(gè)最可能是原始文本的方法之一,就是統(tǒng)計(jì)所有這些文本中的英文單詞的個(gè)數(shù)。這可以使用 PyEnchant 模塊實(shí)現(xiàn):

            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:nn{best_plaintext[:1000]}...')output_file.write(best_plaintext)if __name__ == '__main__':caesar_breaker()

            


            貌似運(yùn)行得很不錯(cuò),但別忘了,好的命令行程序還有個(gè)規(guī)則需要遵守:

            4.A 不是立即完成的任務(wù)應(yīng)當(dāng)顯示進(jìn)度條。

            示例中的文本包含10^4個(gè)單詞,因此該腳本需要大約5秒才能解密。這很正常,因?yàn)樗枰獧z查所有25個(gè)秘鑰,每個(gè)秘鑰都要檢查10^4個(gè)單詞是否出現(xiàn)在英文字典中。

            假設(shè)你要解密的文本包括10^5個(gè)但I(xiàn)C,那么就要花費(fèi)50秒才能輸出結(jié)果,用戶可能會(huì)非常著急。

            因此我建議這種任務(wù)一定要顯示進(jìn)度條。特別是,顯示進(jìn)度條還非常容易實(shí)現(xiàn)。

            下面是個(gè)顯示進(jìn)度條的例子:

            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:nn{best_plaintext[:1000]}...')output_file.write(best_plaintext)if __name__ == '__main__':caesar_breaker()

            你發(fā)現(xiàn)區(qū)別了嗎?可能不太好找,因?yàn)閰^(qū)別真的很小,只有四個(gè)字母:tqdm。

            tqdm 是 Python 庫(kù)的名字,也是它包含的類(lèi)的名字。只需用它包裹一個(gè)可迭代的東西,就能顯示出進(jìn)度條:

            forkeyin tqdm(range(26)):

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

            


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

            我希望這篇文章能讓你在改進(jìn)開(kāi)發(fā)者的體驗(yàn)上多花點(diǎn)時(shí)間。



          關(guān)鍵詞: Python

          評(píng)論


          相關(guān)推薦

          技術(shù)專(zhuān)區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();