1. 项目概述:为什么命令行参数处理是Python开发的必修课
刚接触Python脚本开发时,很多人习惯把配置直接写死在代码里,比如数据库地址、文件路径、运行模式。改一次配置就得翻一次代码,调试起来麻烦不说,脚本也毫无复用性。后来,大家学会了用input()函数交互式输入,但这只适合手动运行,一旦想写个定时任务或者集成到流水线里,立马抓瞎。真正让脚本变得专业、灵活、可复用的关键一步,就是学会优雅地处理命令行参数。这不仅是写个“Hello World”脚本的进阶,更是自动化工具、数据处理管道、后端服务脚本乃至CLI工具开发的基石。
今天要聊的,就是Python里处理命令行参数的两大核心武器:简单直接的sys.argv和功能强大的argparse。sys.argv像是给你一把螺丝刀,能解决基本问题;而argparse则是一整套精密工具箱,从解析、验证、生成帮助信息到子命令管理,一应俱全。理解它们,你就能写出像pip install、git commit那样既专业又用户友好的命令行程序。无论你是想写个自动备份脚本,还是开发一个团队内部的数据处理工具,这套技能都能让你事半功倍。
2. 核心工具对比与选型逻辑
在深入细节之前,我们得先搞清楚什么情况下该用哪个工具。选择不是拍脑袋决定的,而是基于脚本的复杂度、维护成本和用户体验的综合考量。
2.1 sys.argv:轻量级场景的利器
sys.argv是Python标准库sys模块中的一个列表。它的工作方式极其简单:当你通过命令行运行一个Python脚本时,解释器会将命令行的所有部分按空格分割,然后按顺序存入这个列表。
它的核心特点和工作原理:
sys.argv[0]永远是当前脚本的名称。- 后续的每一个参数(以空格分隔)依次成为
sys.argv[1]、sys.argv[2]…… - 它不做任何解析,你传进来什么,它就存什么。引号内的空格会被保留(取决于你的Shell),
--flag和-f对它来说只是普通的字符串。
适用场景:
- 快速原型验证:当你只是临时写个脚本测试某个功能,参数很少(少于3个),且顺序固定。
- 极简工具:脚本功能单一,可能就一两个必选参数,比如
python rename.py old_name new_name。 - 内部使用脚本:脚本仅供你自己或小团队使用,大家对参数顺序心知肚明,不需要帮助文档。
一个典型例子:假设你有个合并文件的脚本,需要两个参数:输入目录和输出文件名。
python merge_files.py ./data_project_2024_merged.csv在脚本里,你可以这样处理:
import sys if len(sys.argv) != 3: # 检查参数数量,脚本名算一个 print(f“用法: {sys.argv[0]} ”) sys.exit(1) # 非零退出码表示错误 input_dir = sys.argv[1] output_file = sys.argv[2] # ... 后续处理逻辑你看,代码直白,但问题也很明显:如果用户忘记参数顺序,或者想用-h看看帮助,脚本只会冷冰冰地报错。
2.2 argparse:生产级脚本的标准配置
argparse模块则是为构建健壮的命令行接口而生。它自动生成帮助和使用说明,支持多种参数类型(必选、可选、标志),能处理短选项(-v)和长选项(--verbose),还能进行参数验证和类型转换。
为什么在稍微复杂的场景下就必须选它?
- 用户体验友好:自动生成格式统一的
-h/--help信息,用户无需阅读源码。 - 代码可维护性强:参数的定义、解析、帮助文本集中管理,与业务逻辑解耦。新增参数只需在定义处添加,无需到处修改解析逻辑。
- 功能全面:
- 位置参数:类似
sys.argv的顺序参数,但可以定义名称和帮助信息。 - 可选参数:以
-或--开头的参数,可以指定默认值。 - 动作类型:除了存储值,还可以触发动作,如
store_true(出现即为True)。 - 类型检查与转换:自动将字符串参数转换为int、float,甚至自定义类型。
- 互斥参数:确保一组参数中只有一个被使用。
- 子命令:支持像
git commit、docker run这样的子命令系统,用于组织复杂功能。
- 位置参数:类似
适用场景:几乎所有需要他人使用或将来可能扩展的脚本。只要参数超过两个,或者存在可选参数,argparse的收益就远超其微小的学习成本。
2.3 选型决策流程图
为了更直观,我们可以用一个简单的决策树来辅助选择:
| 考量维度 | 选择sys.argv | 选择argparse |
|---|---|---|
| 参数数量 | 1-2个,且顺序固定 | 3个及以上,或参数较多 |
| 参数类型 | 全是必填的位置参数 | 包含可选参数、标志(flag) |
| 脚本用户 | 仅自己或极少数熟悉者 | 团队其他成员、不熟悉的用户 |
| 是否需要帮助文档 | 不需要,用法简单或内部约定 | 需要,方便用户查询 |
| 未来是否扩展 | 一次性脚本,用完即弃 | 需要维护、可能会增加功能 |
| 是否需要参数验证 | 可以在业务逻辑里简单判断 | 需要类型检查、范围验证、互斥逻辑 |
实操心得:我的经验法则是,除非这个脚本我确定只用一次,且参数简单到不可能记错,否则一律从
argparse开始。初期多写两行定义代码,能为后期节省大量的调试和解释时间。很多一开始觉得“简单”的脚本,后来都慢慢加上了配置选项、运行模式开关,如果一开始用了sys.argv,重构起来会很痛苦。
3. sys.argv 的深度解析与实战技巧
虽然argparse更强大,但彻底理解sys.argv是基础。它帮你理解命令行参数的本质——一个字符串列表。我们来看几个更深层的用法和常见坑。
3.1 超越基础:灵活处理参数列表
当你的脚本需要处理不定数量的参数时,比如批量处理文件,sys.argv的列表特性就派上用场了。
import sys # 脚本名:process_images.py # 用法:python process_images.py image1.jpg image2.png ... all_image_files = sys.argv[1:] # 使用切片获取除脚本名外的所有参数 if not all_image_files: print(“错误:请至少指定一个图像文件。”) sys.exit(1) for image_path in all_image_files: print(f“正在处理: {image_path}”) # ... 处理每个图像这里,sys.argv[1:]获取了从第二个元素开始的所有参数,非常适合处理同类项目的列表。
3.2 处理带空格的参数与Shell的“魔法”
这是新手最容易踩的坑。在命令行中,空格是默认的分隔符。如果你想传递一个包含空格的路径(如“My Documents/data.csv”),必须用引号包裹。
# 错误:这会被认为是三个参数 python script.py My Documents/data.csv # sys.argv -> [‘script.py’, ‘My’, ‘Documents/data.csv’] # 正确:引号内内容被视为一个参数 python script.py “My Documents/data.csv” # sys.argv -> [‘script.py’, ‘My Documents/data.csv’]在脚本内部,你拿到的是已经去掉外层引号的字符串。这个“去引号”操作是由你的Shell(如bash、zsh、Windows cmd/PowerShell)在调用Python解释器之前完成的,不是Python本身处理的。这一点务必清楚。
3.3 模拟argparse的基础功能
即使只用sys.argv,我们也可以借鉴argparse的思想,实现一些简单的功能,比如支持-o指定输出文件。
import sys output_file = “default_output.txt” # 默认值 input_files = [] # 简陋的解析逻辑 args = sys.argv[1:] i = 0 while i < len(args): if args[i] == “-o” and i + 1 < len(args): output_file = args[i + 1] i += 2 # 跳过 -o 和它的值 else: input_files.append(args[i]) i += 1 if not input_files: print(“错误:未指定输入文件。”) sys.exit(1) print(f“输出文件:{output_file}”) print(f“输入文件:{input_files}”)这个例子揭示了手动解析的复杂性:需要处理选项和值的配对、遍历列表、状态管理。当选项多起来,代码会迅速变得难以维护。这正反衬出argparse的价值。
注意事项:使用
sys.argv时,一定要在业务逻辑开始前做好参数数量的检查和基本验证。一个健壮的脚本应该对用户的错误输入有友好的提示,而不是直接抛出IndexError异常。这也是向argparse的-h帮助功能看齐的第一步。
4. argparse 模块完全指南
现在,让我们进入正题,详细拆解argparse的每一个核心功能。我将按照构建一个命令行程序的自然顺序来讲解。
4.1 基础四步曲:创建、定义、解析、使用
任何使用argparse的脚本都遵循一个清晰的模式。
import argparse # 1. 创建解析器 parser = argparse.ArgumentParser( prog=‘my_tool’, # 程序名(默认用sys.argv[0]) description=‘一个强大的数据处理工具’, # 帮助信息开头描述 epilog=‘感谢使用!如有问题请联系XXX。’ # 帮助信息结尾文字 ) # 2. 定义参数 parser.add_argument(‘input_file’, help=‘输入数据文件的路径’) parser.add_argument(‘-o’, ‘--output’, default=‘result.csv’, help=‘输出文件路径(默认:result.csv)’) parser.add_argument(‘-v’, ‘--verbose’, action=‘store_true’, help=‘开启详细输出模式’) # 3. 解析参数 args = parser.parse_args() # 4. 使用参数 print(f“正在处理输入文件:{args.input_file}”) if args.verbose: print(“详细模式已开启,开始打印日志...”) print(f“结果将输出到:{args.output}”)运行python my_tool.py -h,你会看到自动生成的、格式工整的帮助信息。这四步是基石,后面的所有功能都是在此基础上添加的。
4.2 参数定义详解:add_argument 的核心参数
add_argument()方法是灵魂,它的参数决定了每个命令行参数的行为。
1. 名称或旗帜(name or flags):这是第一个参数,决定参数是“位置的”还是“可选的”。
‘input_file’:这是一个位置参数,调用时必须提供,且顺序对应。‘-o’, ‘--output’:这是一个可选参数。-o是短格式,--output是长格式。通常同时定义,方便用户。
2. action:参数的动作它定义了当解析器在命令行中遇到这个参数时该做什么。
‘store’:默认值。存储参数后面跟随的值。例如-o output.txt,会将‘output.txt’存入args.output。‘store_true’/‘store_false’:标志参数。不需要额外值。出现即置为True或False。常用于开关选项,如--verbose。‘append’:允许多次使用同一参数,值会存入一个列表。例如-I /usr/include -I ./local/include,args.I会是[‘/usr/include’, ‘./local/include’]。‘count’:计算参数出现的次数。例如-vvv,args.v的值会是3。常用于设置日志级别。
3. type:参数类型转换默认所有参数都是字符串。使用type可以自动转换。
parser.add_argument(‘--port’, type=int, default=8080, help=‘服务端口号’) parser.add_argument(‘--ratio’, type=float, help=‘缩放比例’)如果用户输入--port abc,argparse会自动报错,提示abc不是有效的整数。这比自己在业务逻辑里转换和报错要优雅得多。
4. default:默认值当用户没有提供该可选参数时使用的值。对于标志参数(store_true),默认值通常是False。
5. required:是否必填对于可选参数(以-开头的),默认不是必须的。设置required=True可以强制用户必须提供。但谨慎使用,这违反了可选参数的常规预期,通常有更好的设计方式。
6. choices:限定选择范围提供一个可迭代对象(如列表),限定参数只能从中选择。
parser.add_argument(‘--mode’, choices=[‘fast’, ‘standard’, ‘quality’], default=‘standard’, help=‘运行模式’)7. help 和 metavar:帮助信息
help:参数的描述信息,会显示在帮助里。metavar:在帮助信息中替代该参数值的名称。对于位置参数,它影响帮助中的显示;对于可选参数,它影响-h中值占位符的显示。parser.add_argument(‘input’, metavar=‘INPUT_FILE’, help=‘输入文件’) # 帮助中显示: positional arguments: INPUT_FILE 输入文件 parser.add_argument(‘-o’, metavar=‘OUTPUT_PATH’, help=‘输出路径’) # 帮助中显示: -o OUTPUT_PATH 输出路径
4.3 高级功能与实战模式
掌握了基础,我们来看看如何用argparse构建更复杂的接口。
互斥参数组确保一组参数中只有一个被使用,比如指定输入源时,不能同时用--file和--url。
group = parser.add_mutually_exclusive_group(required=True) # required=True表示组里必须有一个被选中 group.add_argument(‘--file’, help=‘从文件读取’) group.add_argument(‘--url’, help=‘从URL读取’)子命令(Sub-commands)这是构建复杂CLI工具(如git、docker)的关键。它将不同功能组织到独立的子解析器中。
parser = argparse.ArgumentParser(prog=‘mycli’) subparsers = parser.add_subparsers(dest=‘command’, help=‘可用命令’, required=True) # 子命令 ‘init’ parser_init = subparsers.add_parser(‘init’, help=‘初始化项目’) parser_init.add_argument(‘project_name’, help=‘项目名称’) # 子命令 ‘build’ parser_build = subparsers.add_parser(‘build’, help=‘构建项目’) parser_build.add_argument(‘--target’, choices=[‘dev’, ‘prod’], default=‘dev’) args = parser.parse_args() if args.command == ‘init’: print(f“正在初始化项目:{args.project_name}”) elif args.command == ‘build’: print(f“正在以 {args.target} 模式构建...”)这样,用户就可以使用mycli init my_project或mycli build --target prod这样的命令了。
从文件读取参数(File Arguments)有时参数很长,可以将其写在文件里,然后用@file.txt的方式传递。argparse默认支持这个特性(来自Unix传统)。
# args.txt 内容: # --verbose # --input data.csv # --output report.pdf python script.py @args.txt解析器会自动读取args.txt文件,将其每一行作为参数插入对应位置。这在自动化部署和测试中非常有用。
实操心得:在定义
add_argument时,我强烈建议为每一个参数都写上help描述,无论它看起来多么不言自明。这不仅是为了用户,更是为了三个月后的你自己。清晰的帮助文档是代码自述能力的一部分。另外,对于有单位的数值参数(如超时时间),在help里注明单位,例如help=‘超时时间,单位:秒 (默认:30)’。
5. 综合实战:构建一个图片处理CLI工具
让我们把上面所有知识融会贯通,设计一个名为imgtool.py的图片处理工具。它支持调整尺寸、转换格式、添加水印等子命令。
5.1 工具设计与参数规划
首先,我们规划工具的结构:
imgtool.py resize:调整图片尺寸。参数:输入文件(必选)、输出文件(可选,默认加后缀)、宽度、高度(宽高至少一个)。imgtool.py convert:转换图片格式。参数:输入文件(必选)、输出格式(必选)、质量(可选)。imgtool.py watermark:添加文字水印。参数:输入文件(必选)、水印文字(必选)、位置(可选)、颜色(可选)。
5.2 代码实现与逐行解析
import argparse import sys def main(): # 创建主解析器 parser = argparse.ArgumentParser( prog=‘imgtool’, description=‘一个多功能命令行图片处理工具’, epilog=‘示例: imgtool resize input.jpg -w 800 -h 600’ ) # 创建子命令管理器, dest=‘command’ 用于后续判断哪个子命令被调用 subparsers = parser.add_subparsers(dest=‘command’, title=‘可用命令’, required=True, help=‘选择要执行的操作’) # ---------- 子命令:resize ---------- parser_resize = subparsers.add_parser(‘resize’, help=‘调整图片尺寸’) # 位置参数:输入文件 parser_resize.add_argument(‘input’, help=‘输入图片文件路径’) # 可选参数:输出文件(有默认逻辑) parser_resize.add_argument(‘-o’, ‘--output’, help=‘输出文件路径。不指定则在原文件名后加“_resized”’) # 可选参数:宽度和高度,至少提供一个。使用‘nargs=‘?’’允许不提供值(使用const),但这里我们用更直观的方式。 # 我们使用互斥组来模拟“至少一个”的逻辑,但更简单的做法是解析后检查。 parser_resize.add_argument(‘-W’, ‘--width’, type=int, help=‘目标宽度(像素)’) parser_resize.add_argument(‘-H’, ‘--height’, type=int, help=‘目标高度(像素)’) # 可选标志:是否保持宽高比 parser_resize.add_argument(‘--keep-ratio’, action=‘store_true’, help=‘调整大小时保持宽高比’) # ---------- 子命令:convert ---------- parser_convert = subparsers.add_parser(‘convert’, help=‘转换图片格式’) parser_convert.add_argument(‘input’, help=‘输入图片文件路径’) # 输出文件这里我们用 -o, 但也可以根据输入文件和格式自动生成。 parser_convert.add_argument(‘-o’, ‘--output’, help=‘输出文件路径。不指定则替换原文件扩展名’) # 必选参数:目标格式,限定选择范围 parser_convert.add_argument(‘-f’, ‘--format’, required=True, choices=[‘jpg’, ‘png’, ‘webp’, ‘bmp’], help=‘目标图片格式’) # 可选参数:质量(1-100),仅对某些格式有效 parser_convert.add_argument(‘-q’, ‘--quality’, type=int, default=85, choices=range(1, 101), metavar=‘[1-100]’, help=‘输出质量(默认:85)’) # ---------- 子命令:watermark ---------- parser_watermark = subparsers.add_parser(‘watermark’, help=‘添加文字水印’) parser_watermark.add_argument(‘input’, help=‘输入图片文件路径’) parser_watermark.add_argument(‘-o’, ‘--output’, help=‘输出文件路径。不指定则在原文件名后加“_watermarked”’) # 必选参数:水印文字 parser_watermark.add_argument(‘-t’, ‘--text’, required=True, help=‘水印文字内容’) # 可选参数:位置,有默认值 parser_watermark.add_argument(‘-p’, ‘--position’, default=‘bottom-right’, choices=[‘top-left’, ‘top-right’, ‘center’, ‘bottom-left’, ‘bottom-right’], help=‘水印位置(默认:bottom-right)’) # 可选参数:颜色,格式为 ‘255,255,255,128’ (RGBA) parser_watermark.add_argument(‘-c’, ‘--color’, default=‘255,255,255,128’, help=‘水印颜色和透明度(RGBA, 默认:半透明白色)’) # 解析所有参数 args = parser.parse_args() # 根据子命令分发到不同的处理函数 if args.command == ‘resize’: handle_resize(args) elif args.command == ‘convert’: handle_convert(args) elif args.command == ‘watermark’: handle_watermark(args) def handle_resize(args): “”“处理resize子命令”“” # 1. 参数验证:宽高至少提供一个 if args.width is None and args.height is None: print(“错误:必须指定至少 --width 或 --height 中的一个参数。”) sys.exit(1) # 2. 处理默认输出文件名 output_path = args.output if output_path is None: # 简单的默认命名逻辑:在原名基础上添加 ‘_resized’ from pathlib import Path input_path = Path(args.input) output_path = input_path.parent / f“{input_path.stem}_resized{input_path.suffix}” # 3. 模拟核心逻辑 print(f“[Resize] 处理中...”) print(f“ 输入文件:{args.input}”) print(f“ 输出文件:{output_path}”) print(f“ 目标尺寸:{args.width or ‘自动’} x {args.height or ‘自动’}”) print(f“ 保持比例:{‘是’ if args.keep_ratio else ‘否’}”) # 这里可以调用PIL等库进行实际的图片缩放操作 # from PIL import Image # ... def handle_convert(args): “”“处理convert子命令”“” # 处理默认输出文件名(根据格式) output_path = args.output if output_path is None: from pathlib import Path input_path = Path(args.input) # 替换后缀名为新格式 output_path = input_path.parent / f“{input_path.stem}.{args.format}” print(f“[Convert] 处理中...”) print(f“ 输入文件:{args.input}”) print(f“ 输出文件:{output_path}”) print(f“ 目标格式:{args.format}”) print(f“ 输出质量:{args.quality}”) # 调用图片库进行格式转换 def handle_watermark(args): “”“处理watermark子命令”“” output_path = args.output if output_path is None: from pathlib import Path input_path = Path(args.input) output_path = input_path.parent / f“{input_path.stem}_watermarked{input_path.suffix}” print(f“[Watermark] 处理中...”) print(f“ 输入文件:{args.input}”) print(f“ 输出文件:{output_path}”) print(f“ 水印文字:{args.text}”) print(f“ 位置:{args.position}”) print(f“ 颜色(RGBA):{args.color}”) # 调用图片库添加水印 if __name__ == ‘__main__’: main()5.3 关键设计解析与经验点
- 子命令的必要性:当工具功能超过3个,且功能相对独立时,子命令是组织代码和用户界面的最佳方式。它比用一堆互斥的
--mode参数清晰得多。 - 默认输出路径的逻辑:这是一个非常实用的设计。我们不是简单地要求用户必须提供
-o,而是提供了智能的默认命名规则(添加后缀)。这提升了工具的易用性。实现时使用了pathlib模块,它是处理文件路径的现代、跨平台首选。 - 参数的后验证:
argparse能处理类型和选择范围验证,但无法处理复杂的逻辑依赖(如“宽高至少一个”)。这类验证需要在parse_args()之后,业务逻辑开始前手动进行。这是argparse使用中的一个常见模式。 - 帮助信息的分组:通过
add_subparsers的title和help参数,以及子解析器自己的help,帮助信息变得层次分明,用户一眼就能看懂工具结构。
运行一下看看效果:
python imgtool.py -h python imgtool.py resize -h python imgtool.py resize photo.jpg -W 800 --keep-ratio6. 常见问题、调试技巧与进阶思考
即使掌握了基本用法,在实际开发中还是会遇到一些坑。这里记录了一些常见问题和我的解决经验。
6.1 常见错误与排查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 运行脚本没有任何输出,也不报错 | 可能忘记了调用parser.parse_args(),或者parse_args()之后没有使用args对象。 | 检查代码最后是否调用了args = parser.parse_args(),并且后续逻辑使用了args.some_argument。 |
提示error: the following arguments are required: ... | 定义了位置参数(没有-或--前缀)但没有提供。或者子命令的required=True但没给子命令。 | 检查命令行输入是否正确包含了所有必须的位置参数。对于子命令,确保add_subparsers设置了required=True时,用户必须指定一个子命令。 |
布尔型标志参数总是False | 可能错误地使用了action=‘store’并提供了默认值,而不是action=‘store_true’。 | 对于开关标志,使用action=‘store_true’(默认False,出现则为True)或action=‘store_false’(默认True,出现则为False)。 |
| 参数值被错误地解析(如带短横线的字符串被当成新选项) | 如果参数值以-开头,argparse会误认为它是一个新选项。 | 使用--分隔符。在命令行中,--之后的任何内容都会被当作位置参数处理。例如:python script.py --input --special-file.txt。 |
使用nargs=‘*’或nargs=‘+’时,参数列表被意外截断 | 如果可变长度参数后面还有其他位置参数,argparse可能无法区分边界。 | 将可变长度参数放在参数定义的最后。或者,更可靠的方法是使用nargs=‘*’并放在最后,或者使用子命令来分隔不同功能模块。 |
自定义type函数抛出异常,错误信息不友好 | argparse会捕获type函数抛出的任何异常(如ValueError,TypeError)并以自己的格式显示。 | 在自定义type函数中,抛出argparse.ArgumentTypeError异常,这样可以提供更清晰的错误信息。 |
6.2 调试技巧:看清argparse做了什么
当你觉得参数解析结果不符合预期时,不要猜,直接打印出来看。
args = parser.parse_args() print(“[DEBUG] 解析后的命名空间:”, args) print(“[DEBUG] 转换为字典:”, vars(args)) # 或者直接检查某个参数 if args.verbose: print(“[DEBUG] 详细模式开启,所有参数:”, vars(args))vars(args)将命名空间对象转换为字典,便于查看所有属性和值。
6.3 进阶话题:自定义Action与Type
有时内置动作和类型不够用,你可以自定义。
自定义Action:比如你想让一个参数同时设置多个属性。
class SetLogLevelAction(argparse.Action): def __call__(self, parser, namespace, values, option_string=None): setattr(namespace, self.dest, values) # 存储原始值 # 同时设置一个衍生的verbose标志 if values == ‘DEBUG’: setattr(namespace, ‘verbose’, True) else: setattr(namespace, ‘verbose’, False) parser.add_argument(‘--log-level’, choices=[‘DEBUG’, ‘INFO’, ‘WARNING’], action=SetLogLevelAction, default=‘INFO’)自定义Type:进行更复杂的验证和转换。
def valid_port_number(value): try: port = int(value) except ValueError: raise argparse.ArgumentTypeError(f“{value} 不是有效的整数”) if not (1 <= port <= 65535): raise argparse.ArgumentTypeError(f“端口号 {port} 必须在 1-65535 之间”) return port parser.add_argument(‘-p’, ‘--port’, type=valid_port_number, default=8080)6.4 与配置文件的结合
大型工具通常需要结合命令行参数和配置文件。一个常见的模式是:命令行参数优先级最高,用于覆盖配置文件中的默认设置。
你可以使用像configparser(用于INI文件)、json、yaml(需安装PyYAML)等模块来读取配置文件。基本流程是:
- 读取配置文件,加载默认设置到一个字典。
- 使用
argparse解析命令行参数。 - 将命令行参数(非默认值)更新到配置字典中,覆盖文件配置。
- 程序使用最终的配置字典。
这实现了配置的层级结构:默认值(代码内) < 配置文件 < 命令行参数。
最后,我想说的是,处理命令行参数的本质是设计用户与程序交互的接口。sys.argv给了你最基本的材料,而argparse提供了一套完整的设计语言。花时间设计清晰、直观、有帮助的命令行界面,和你花时间设计函数API、软件架构一样重要。一个好的CLI能让你的工具更容易被接受、被记住,也让你自己用得更加顺手。下次写脚本时,不妨先从import argparse开始。