当前位置:

首页 > 编程开发 > 如何使用click库创建完美的Python命令行程序?

如何使用click库创建完美的Python命令行程序?

Python程序员的主要工作是写命令行程序,即直接在终端运行的脚本。随着项目规模增长,我们希望创建有效的命令行接口,通过提供不同的参数,解决不同的问题,而不是每次都修改源代码。为了实现这一目标,我总结了四条原则,希望对大家有所帮助:命令行参数应提供默认值处理所有可能的参数错误,包括缺少参数,数据类型错误,无法找到文件等撰写完善的文档,解释参数的含义以及如何设置使用进度条显示长时间运行的任务一个简单的例子让我们将这些规则应用于一个具体的案例:一个使用Caesarcipher加密和解密消息的脚本。

如何使用click库创建完美的Python命令行程序?


Python程序员的主要工作是写命令行程序,即直接在终端运行的脚本。随着项目规模增长,我们希望创建有效的命令行接口,通过提供不同的参数,解决不同的问题,而不是每次都修改源代码。

为了实现这一目标,我总结了四条原则,希望对大家有所帮助:

  • 命令行参数应提供默认值
  • 处理所有可能的参数错误,包括缺少参数,数据类型错误,无法找到文件等
  • 撰写完善的文档,解释参数的含义以及如何设置
  • 使用进度条显示长时间运行的任务

一个简单的例子

让我们将这些规则应用于一个具体的案例:一个使用Caesar cipher加密和解密消息的脚本。

假设我们编写了一个encrypt函数,如下所示。现在要创建一个的脚本来加密和解密消息。

脚本允许用户选择:模式(加密或解密),密钥。前者的默认值是加密,后者的默认值是1。这一切都通过命令行参数实现。

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

初学者方法:sys.argv

脚本需要先获取命令行参数的值,让我们先用最简单的sys.argv实现。

sys.argv是一个列表,包含了用户在运行脚本时输入的所有参数(包括脚本名字本身)。

在终端输入以下指令:

> python caesar_script.py --key 23 --decrypt my secret message
pb vhfuhw phvvdjh

sys.argv列表包括:

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

为了获得参数值,需要遍历参数列表,寻找一个 '--key' (或 '-k' )来获取密钥值,并寻找一个 '--decrypt' 获取模式。

import sys
from caesar_encryption import encryp
def caesar():
key = 1
is_error = False
for 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]
break
for index, arg in enumerate(sys.argv):
if arg in ['--encrypt', '-e']:
del sys.argv[index]
break
if arg in ['--decrypt', '-d']:
key = -key
del sys.argv[index]
break
if len(sys.argv) == 1:
is_error = True
else:
for arg in sys.argv:
if arg.startswith('-'):
is_error = True
if is_error:
print(f'Usage: python {sys.argv[0]} [ --key  ] [ --encrypt|decrypt ] ')
else:
print(encrypt(' '.join(sys.argv[1:]), key))
if __name__ == '__main__':
caesar()

代码遵循我们一开始提出的原则:

有一个默认键值和一个默认模式

处理基本错误(不提供输入文本或未知参数)

在参数错误或在不带参数的情况下调用脚本时,打印简洁的提示信息

> python caesar_script_using_sys_argv.py
Usage: python caesar.py [ --key  ] [ --encrypt|decrypt ] 

但是这个版本的脚本相当长(39行,不包括加密函数),而且代码非常丑陋。

是否有更好的方法来解析命令行参数?

进入argparse

argparse是用于解析命令行参数的Python标准库模块。

修改脚本,使用argparse解析命令行参数:

import argparse
from caesar_encryption import encrypt
def 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.key
if args.decrypt:
key = -key
cyphertext = encrypt(text_string, key)
print(cyphertext)
if __name__ == '__main__':
caesar()

代码仍然遵守我们提出的原则,并且比手动解析命令行参数提供更精确的文档和更具交互性的错误处理。

> python caesar_script_using_argparse.py --encode My message
usage: caesar_script_using_argparse.py [-h] [-e | -d] [-k KEY] [text [text ...]]
caesar_script_using_argparse.py: error: unrecognized arguments: --encode
> python caesar_script_using_argparse.py --help
usage: caesar_script_using_argparse.py [-h] [-e | -d] [-k KEY] [text [text ...]]positional arguments:
text
optional arguments:
-h, --help show this help message and exit
-e, --encrypt
-d, --decrypt
-k KEY, --key KEY

脚本的第7行到第13行代码定义了命令行参数,但它们不是很优雅:太冗长且程序化,我们可以用更紧凑和声明性的方式完成。

使用click创建更好的命令行接口

幸运的是有一个三方库click用于创建命令行接口,它不仅提供比argparse更多的功能, 而且代码风格更漂亮。用click替换argparse,继续优化脚本。

import click
from 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定义互斥的选项 ,最终以布尔值传递给程序。

click.echo是click库提供的基础函数,功能类似于print,但提供更强大的功能,例如调整打印到控制台的文本的颜色。

从本地文件读取输入

命令行参数接收的值是将被加密的最高机密消息,所以如果要求用户直接在终端中输入纯文本,可能会引发安全顾虑。

一种更安全的方法是使用隐藏提示,或者从本地文件读取文本 ,这对于长文本来说更加实用。

这个想法同样适用于输出:用户可以将其保存到文件中,或者在终端中打印出来。让我们继续优化脚本。

import click
from 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()

由于脚本变得更复杂,我们创建了参数文档(通过定义click.option装饰器的help参数实现),详细解释参数的功能,效果如下。

> python caesar_script_v2.py --help
Usage: caesar_script_v2.py [OPTIONS]
Options:
--input_file FILENAMEFile in which there is the text you want to encrypt/decrypt. If not provided, a prompt will allow you to type the input text.
--output_file FILENAME File in which the encrypted/decrypted text will be written. If not provided, the output text will just be printed.
-d, --decrypt / -e, --encryptWhether you want to encrypt the input text or decrypt it.
-k, --key INTEGERThe numeric key to use for the caesar encryption / decryption.
--help Show this message and exit.

我们有两个新的参数input_file和output_file,类型是click.File,click会用正确的模式打开文件并处理可能发生的错误。例如找不到文件:

> python caesar_script_v2.py --decrypt --input_file wrong_file.txt
Usage: caesar_script_v2.py [OPTIONS]
Error: Invalid value for "--input_file": Could not open file: wrong_file.txt: No such file or directory

如果未提供input_file,则我们用click.prompt,在命令行创建提示窗口,让用户直接输入文本,该提示对于加密模式将是隐藏的。效果如下:

> python caesar_script_v2.py --encrypt --key 2
Enter a text: **************
yyy.ukectc.eqo

假设你是一名黑客:想要解密一个用凯撒加密过的密文,但你不知道秘钥是什么。最简单的策略就是用所有可能的秘钥调用解密函数 25 次,阅读解密结果,看看哪个是合理的。但你很聪明,而且也很懒,所以你想让整个过程自动化。确定解密后的 25 个文本哪个最可能是原始文本的方法之一,就是统计所有这些文本中的英文单词的个数。这可以使用 PyEnchant 模块实现:

import click
import enchant
from 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,
)
def caesar_breaker(input_file, output_file):
cyphertext = input_file.read()
english_dictionnary = enchant.Dict("en_US")
max_number_of_english_words = 0
for key in range(26):
plaintext = encrypt(cyphertext, -key)
number_of_english_words = 0
for word in plaintext.split(' '):
if word and english_dictionnary.check(word):
number_of_english_words += 1
if number_of_english_words > max_number_of_english_words:
max_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()

如何使用click库创建完美的Python命令行程序?

使用进度条

示例中的文本包含10^4个单词,因此该脚本需要大约5秒才能解密。这很正常,因为它需要检查所有25个秘钥,每个秘钥都要检查10^4个单词是否出现在英文字典中。

假设你要解密的文本包括10^5个单词,那么就要花费50秒才能输出结果,用户可能会非常着急。因此我建议这种任务一定要显示进度条。特别是,显示进度条还非常容易实现。下面是个显示进度条的例子:

import click
import enchant
from tqdm import tqdm
from 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,
)
def caesar_breaker(input_file, output_file):
cyphertext = input_file.read()
english_dictionnary = enchant.Dict("en_US")
best_number_of_english_words = 0
for key in tqdm(range(26)):
plaintext = encrypt(cyphertext, -key)
number_of_english_words = 0
for word in plaintext.split(' '):
if word and english_dictionnary.check(word):
number_of_english_words += 1
if 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()

这里使用了tqdm库,tqdm.tqdm类可以将任何可迭代对象转化为一个进度条。click也提供了类似的接口来创建进度条(click.progress_bar),但我觉得它不如tqdm好用。

如何使用click库创建完美的Python命令行程序?

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
作者最新文章
编程开发 命令行 Python
相关文章 更多
codekit环境配置指南从安装到环境搭建完整教程
codekit环境配置指南从安装到环境搭建完整教程

详解 CodeKit 在 macOS 下的安装步骤、项目导入方法、Sass与JavaScript编译设置及浏览器自动刷新功能,助您快速搭建高效的前端开发环境。

codex安装windows 命令行完整操作教程
codex安装windows 命令行完整操作教程

详解Windows环境下安装OpenAI Codex CLI的步骤,包括WSL环境检查、Node.js/npm配置、npm全局安装命令及首次启动验证,适合开发者快速上手。

NativeRest环境配置要求与完整操作教程
NativeRest环境配置要求与完整操作教程

学习如何配置 NativeRest REST API 客户端。涵盖 Windows/macOS/Linux 安装后的工作区创建、环境变量管理、请求编辑及响应查看步骤,帮助开发者快速完成基础环境搭建与连通性测试。

CSS设置透明度的注意事项有哪些?opacity属性详解
CSS设置透明度的注意事项有哪些?opacity属性详解

深入解析CSS中设置透明度的核心属性opacity,剖析子元素继承、事件穿透、层叠上下文等关键注意事项,并提供与rgba、hsla的实用选型对比。

flutter页面传值到后台的方法及示例代码
flutter页面传值到后台的方法及示例代码

flutter页面传值到后台的完整实现方法及示例代码,帮助读者快速掌握相关技术要点。

Java 8至21新特性代码写法对比:Lambda、Record与Switch
Java 8至21新特性代码写法对比:Lambda、Record与Switch

本文通过具体的旧版与新版代码对比,详细剖析Java 8引入的Lambda表达式、Java 14/16引入的Record类,以及Java 12至21逐步演进完善的Switch表达式与模式匹配,展示代码简化路径与避坑要点。

AI智能体开发培训课程学什么及实战内容介绍
AI智能体开发培训课程学什么及实战内容介绍

系统梳理AI智能体开发培训的核心知识模块、技术栈选型与典型实战项目,解析低代码平台与纯代码框架的差异,提供从零构建可落地智能体的完整学习与实施路径。

Java子类未实现抽象方法编译错误修复指南
Java子类未实现抽象方法编译错误修复指南

针对Java开发中常见的“子类未实现抽象方法”编译错误,深入分析报错原因,提供重写实现、声明抽象子类两种标准修复路径,并总结参数签名、访问修饰符等典型避坑要点。

解决PHP递归报错:max_nesting_level限制与内存溢出处理
解决PHP递归报错:max_nesting_level限制与内存溢出处理

遇到PHP递归报错时,不要盲目调大max_nesting_level。本文教你区分Xdebug限制、内存耗尽和正则递归错误,提供代码级的终止条件优化与迭代替代方案,彻底解决栈溢出问题。

PHP递归中static变量与引用传递的常见陷阱及调试
PHP递归中static变量与引用传递的常见陷阱及调试

本文分析PHP递归中static变量导致的状态污染及引用传递引发的共享数据修改问题。提供具体的代码复现、缓存键设计建议及调试打印技巧,帮助开发者避免隐蔽的逻辑错误。

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
photoshop
photoshop
Windows、macOS 、 iPad

Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

Blender
Blender
Windows、macOS 和 Linux

Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

WINDOWS 更多
3dmax(3ds max)
3dmax(3ds max)
Windows

Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

photoshop
photoshop
Windows、macOS 、 iPad

Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

Blender
Blender
Windows、macOS 和 Linux

Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。