當創建一個應用程式時,通常希望能夠告訴你的應用程式如何做些什麼。有兩種流行的方法來完成這項任務,你可以讓應用程式接受命令列參數,或是建立一個圖形化的使用者介面。有些應用程式兩者都支援。
當你需要在伺服器上執行你的程式碼時,命令列介面很有幫助。大多數伺服器沒有圖形化介面,特別當它們是Linux伺服器時。在這種情況下,即使你想運行圖形使用者介面,你也可能無法運行。
Python 有一個叫做 argparse 內建的函式庫,可以用它來建立一個命令列介面。在這篇文章中,你將會了解以下內容。
argparse模組的內容比本文要介紹的多得多。如果你想知道更多關於它的信息,你可以查看文件。
現在是時候開始從命令列解析參數了。
在學習如何使用 argparse 之前,最好知道還有一個方法可以向 Python 腳本傳遞參數。你可以向 Python 腳本傳遞任何參數,並透過使用 sys 模組存取這些參數。
為了了解它是如何運作的,建立一個名為sys_args.py 的文件,並在其中輸入以下程式碼:
# sys_args.py import sys def main(): print('You passed the following arguments:') print(sys.argv) if __name__ == '__main__': main()
這段程式碼導入sys並列印出sys.argv中的任何內容。 argv屬性包含了所有傳遞給腳本的東西的列表,其中第一項是腳本本身。
下面是一個例子,說明當你執行這段程式碼和幾個參數的時候會發生什麼事。
$ python3 sys_args.py --s 45 You passed the following arguments: ['sys_args.py', '--s', '45']
使用sys.argv的問題是,你無法控制可以傳遞給你的應用程式的參數。
這就是為什麼使用argparse 是使用Python 標準函式庫的方法。 argparse 模組是非常強大且有用的。讓我們想一想,一個命令列應用程式所遵循的常見過程。
下面是一個關於如何工作的通用例子。繼續建立file_parser.py並新增以下程式碼。
# file_parser.py import argparse def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: print(f'Creating {output_file}') def main(): parser = argparse.ArgumentParser('File parser') parser.add_argument('--infile', help='Input file') parser.add_argument('--out', help='Output file') args = parser.parse_args() if args.infile: file_parser(args.infile, args.out) if __name__ == '__main__': main()
file_parser()函數就是進行解析的邏輯所在。在這個例子中,它只接收一個檔案名,並將其列印出來。 output_file參數的預設值是一個空字串。
程式的重點在 main()。在這裡你創建了一個 argparse.ArgumentParser()的實例,並給你的解析器起了一個名字。然後你加入兩個參數, --infile和 --out。為了使用這個解析器,你需要呼叫 parse_args(),它將傳回傳遞給你的程式的任何有效參數。最後,你要檢查使用者是否使用了 --infile 標誌。如果他們使用了,那麼你就執行 file_parser()。
下面是你如何在你的終端機中執行程式碼。
$ python file_parser.py --infile something.txt Processing something.txt Finished processing
在這裡,你用 --infile標誌和一個檔案名稱來執行你的腳本。這將運行 main(),然後呼叫 file_parser()。
下一步是使用你在程式碼中聲明的兩個命令列參數來嘗試執行你的應用程式。
$ python file_parser.py --infile something.txt --out output.txt Processing something.txt Finished processing Creating output.txt
這次,你得到了一個額外的輸出行,提到了輸出檔名。這代表你的程式碼邏輯中的一個分支。當你指定一個輸出檔時,你可以讓你的程式碼透過使用一個新的程式碼區塊或一個函數來產生該檔案。如果你不指定一個輸出文件,那麼那個程式碼區塊就不會運作。
當你使用argparse創建你的命令列工具時,你可以輕鬆地添加訊息,當你的用戶不確定如何正確地與你的程式互動時,可以幫助他們。
現在是時候找出如何從你的應用程式中獲得幫助了
argparse庫將使用你在創建每個參數時提供的信息,自動為你的應用程式創建一個有用的信息。這裡是程式碼:
# file_parser.py import argparse def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: print(f'Creating {output_file}') def main(): parser = argparse.ArgumentParser('File parser') parser.add_argument('--infile', help='Input file') parser.add_argument('--out', help='Output file') args = parser.parse_args() if args.infile: file_parser(args.infile, args.out) if __name__ == '__main__': main()
現在試著用 -h標誌運行這段程式碼,你應該看到以下內容。
$ file_parser.py -h usage: File parser [-h] [--infile INFILE] [--out OUT] optional arguments: -h, --help show this help message and exit --infile INFILEInput file --out OUTOutput file
add_argument()的幫助參數被用來建立上面的幫助資訊。 argparse會自動新增 -h和 -help選項。你可以透過給它一個描述和後記來使你的幫助資訊更豐富。
讓我們用它們來改進你的幫助資訊。首先,把上面的程式碼複製到一個新的檔案中,命名為 file_parser_with_description.py,然後把它修改成下面的樣子。
# file_parser_with_description.py import argparse def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: print(f'Creating {output_file}') def main(): parser = argparse.ArgumentParser( 'File parser', description='PyParse - The File Processor', epilog='Thank you for choosing PyParse!', ) parser.add_argument('--infile', help='Input file for conversion') parser.add_argument('--out', help='Converted output file') args = parser.parse_args() if args.infile: file_parser(args.infile, args.out) if __name__ == '__main__': main()
在這裡,把description和epilog參數傳遞給ArgumentParser。也更新了 add_argument()的幫助參數,使其更具描述性。
在做了這些修改之後,當你用 -h或 --help執行這個腳本時,你會看到以下輸出。
$ python file_parser_with_description.py -h usage: File parser [-h] [--infile INFILE] [--out OUT] PyParse - The File Processor optional arguments: -h, --help show this help message and exit --infile INFILEInput file for conversion --out OUTConverted output file Thank you for choosing PyParse!
现在可以在你的帮助输出中看到新的description 和epilog。这给了你的命令行程序一些额外的修饰。
你也可以通过ArgumentParser的 add_help参数在你的应用程序中完全禁用帮助。如果你认为你的帮助文本过于冗长,你可以像这样禁用它。
# file_parser_no_help.py import argparse def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: print(f'Creating {output_file}') def main(): parser = argparse.ArgumentParser( 'File parser', description='PyParse - The File Processor', epilog='Thank you for choosing PyParse!', add_help=False, ) parser.add_argument('--infile', help='Input file for conversion') parser.add_argument('--out', help='Converted output file') args = parser.parse_args() if args.infile: file_parser(args.infile, args.out) if __name__ == '__main__': main()
通过将 add_help设置为 False,你将禁用 -h和 --help标志。
你可以看到下面的演示。
$ python file_parser_no_help.py --help usage: File parser [--infile INFILE] [--out OUT] File parser: error: unrecognized arguments: --help
在下一节中,你将学习如何为你的参数添加别名!
别名是一个花哨的词,指的是使用一个替代的标志来做同样的事情。例如,你知道你可以使用 -h和 --help来访问程序的帮助信息。-h是 --help的别名,反之亦然。
看看 main()里面的 parser.add_argument()方法有什么变化。
# file_parser_aliases.py import argparse def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: print(f'Creating {output_file}') def main(): parser = argparse.ArgumentParser( 'File parser', description='PyParse - The File Processor', epilog='Thank you for choosing PyParse!', add_help=False, ) parser.add_argument('-i', '--infile', help='Input file for conversion') parser.add_argument('-o', '--out', help='Converted output file') args = parser.parse_args() if args.infile: file_parser(args.infile, args.out) if __name__ == '__main__': main()
这里你改变了第一个 add_argument(),除了接受 -infile之外,还接受了 -i,你还在第二个 add_argument()中加入了 -o。这样就可以使用两个新的快捷标志来运行你的代码。
下面是一个例子。
$ python3 file_parser_aliases.py -i something.txt -o output.txt Processing something.txt Finished processing Creating output.txt
如果你去看argparse文档,你会发现也可以给子解析器添加别名。子解析器是一种在你的应用程序中创建子命令的方法,这样它就可以做其他事情。一个很好的例子是Docker,一个虚拟化或容器应用程序。它有一系列的命令,你可以在docker下运行,以及docker compose等等。这些命令中的每一个都有独立的子命令,你都可以使用。
下面是一个典型的docker命令,用于运行一个容器。
docker exec -it container_name bash
这将用docker启动一个容器。而如果你要使用docker compose,你将使用一组不同的命令。exec和compose是subparsers的例子。
有时你需要让你的应用程序接受一些参数,但不接受其他参数。例如,你可能想限制你的应用程序,使它只能创建或删除文件,而不是同时创建和删除。
argparse模块提供了 add_mutually_exclusive_group()方法,它就是这样做的。
将你的两个参数添加到一个组对象中,使其相互排斥,如下面的例子。
# file_parser_exclusive.py import argparse def file_parser(input_file, output_file=''): print(f'Processing {input_file}') print('Finished processing') if output_file: print(f'Creating {output_file}') def main(): parser = argparse.ArgumentParser( 'File parser', description='PyParse - The File Processor', epilog='Thank you for choosing PyParse!', add_help=False, ) group = parser.add_mutually_exclusive_group() group.add_argument('-i', '--infile', help='Input file for conversion') group.add_argument('-o', '--out', help='Converted output file') args = parser.parse_args() if args.infile: file_parser(args.infile, args.out) if __name__ == '__main__': main()
首先,你创建了一个相互排斥的组。然后,你把 -i和 -o参数添加到组中,而不是添加到解析器对象中。现在这两个参数是互斥的。
下面是当你试图用这两个参数运行你的代码时发生的情况。
$ python3 file_parser_exclusive.py -i something.txt -o output.txt usage: File parser [-i INFILE | -o OUT] File parser: error: argument -o/--out: not allowed with argument -i/--infile
用这两个参数运行你的代码,会使你的解析器向用户显示一条错误信息,解释他们做错了什么。
在涵盖了所有这些与使用argparse有关的信息之后,你已经准备好应用你的新技能来创建一个简单的搜索工具了
在开始创建一个应用程序之前,弄清楚你要完成的任务总是好的。你在本节中想要建立的应用程序应该能够搜索特定文件类型的文件。为了使它更有趣,你可以添加一个额外的参数,让你也能选择性地搜索特定的文件大小。
你可以使用 Python 的 glob 模块来搜索文件类型。你可以在这里阅读关于这个模块的所有信息。
https://docs.python.org/3/library/glob.html
还有一个 fnmatch 模块,glob 自己也使用它。你现在应该使用 glob,因为它更容易使用,但是如果你有兴趣写一些更专业的东西,那么 fnmatch 可能是你正在寻找的。
然而,由于你希望能够通过文件大小来选择性地过滤返回的文件,你可以使用 pathlib,它包括一个类似 glob 的接口。glob 模块本身并不提供文件大小的信息。
你可以先创建一个名为 pysearch.py 的文件并输入以下代码。
# pysearch.py import argparse import pathlib def search_folder(path, extension, file_size=None): """ Search folder for files """ folder = pathlib.Path(path) files = list(folder.rglob(f'*.{extension}')) if not files: print(f'No files found with {extension=}') return if file_size is not None: files = [ f for f in files if f.stat().st_size >= file_size ] print(f'{len(files)} *.{extension} files found:') for file_path in files: print(file_path)
在上面的代码片段中,首先导入了argparse和pathlib。接下来,创建了search_folder()函数,它接收了三个参数。
把路径变成pathlib.Path对象,然后使用其rglob()方法在文件夹中搜索用户传入的扩展名。如果没有找到文件,就向用户打印一个有意义的信息,然后退出。
如果找到了任何文件,就检查是否已经设置了filesize。如果它被设置了,就用一个list comprehension来过滤出小于指定的filesize的文件。
接下来,打印出找到的文件的数量,最后在这些文件上循环,打印出它们的名字。
为了使这一切正常工作,需要创建一个命令行界面。你可以通过添加一个包含argparse代码的main()函数来做到这一点,像这样。
def main(): parser = argparse.ArgumentParser( 'PySearch', description='PySearch - The Python Powered File Searcher', ) parser.add_argument('-p', '--path', help='The path to search for files', required=True, dest='path') parser.add_argument('-e', '--ext', help='The extension to search for', required=True, dest='extension') parser.add_argument('-s', '--size', help='The file size to filter on in bytes', type=int, dest='size', default=None) args = parser.parse_args() search_folder(args.path, args.extension, args.size) if __name__ == '__main__': main()
这个ArgumentParser()有三个参数,与你传递给search_folder()的参数相对应。让--path和--ext参数成为必需的,而让--size参数成为可选的。注意,--size参数被设置为type=int,这意味着你不能把它传成字符串。
add_argument()函数有一个新的参数。它是dest参数,可以用它来告诉你的参数分析器在哪里保存传递给它们的参数。
下面是一个脚本运行的例子。
$ python3 pysearch.py -p /Users/michael/Dropbox/python101code/chapter32_argparse -e py -s 650 6 *.py files found: /Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_aliases2.py /Users/michael/Dropbox/python101code/chapter32_argparse/pysearch.py /Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_aliases.py /Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_with_description.py /Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_exclusive.py /Users/michael/Dropbox/python101code/chapter32_argparse/file_parser_no_help.py
现在试试用-s和一个字符串来运行它。
$ python3 pysearch.py -p /Users/michael/Dropbox/python101code/chapter32_argparse -e py -s python usage: PySearch [-h] -p PATH -e EXTENSION [-s SIZE] PySearch: error: argument -s/--size: invalid int value: 'python'
这次我们收到了一个错误,因为-s和-size只接受整数。在你自己的机器上运行一下这段代码,看看当你使用-s和整数时,它是否按你想要的方式工作。
这里有一些想法,你可以用来改进你的代码版本。
更好地处理扩展文件。现在,它将接受 *.py,这不会像你期望的那样工作。
更新代码,以便你可以一次搜索多个扩展名
更新代码,以便对文件大小的范围进行过滤(例如,1MB-5MB)。
还有很多其他的功能和改进,你可以添加到这个代码中,比如添加错误处理或单元测试。
argparse模块功能齐全,可以用来创建庞大、灵活的命令行应用程序。在本章中,你了解了以下内容。
你可以用argparse模块做更多的事情,不完全包括本章所讲的。请务必查看文档以了解全部细节。现在去试试吧。你会发现,一旦你掌握了使用argparse的窍门,你就可以创建一些非常整洁的应用程序了。
以上是用 Python 內建程式庫建立一個命令列應用程式的詳細內容。更多資訊請關注PHP中文網其他相關文章!