▶️Python argparse 模块详解
官方文档:argparse --- 用于命令行选项、参数和子命令的解析器
一、引言与模块概述
1️⃣ 命令行参数解析的重要性
在开发Python命令行工具时,处理用户输入的参数是基本需求。argparse模块是Python标准库中用于解析命令行参数的推荐工具,它提供了:
-
清晰的参数定义接口
-
自动生成帮助文档
-
类型检查和错误提示
-
子命令支持等高级功能
2️⃣ 一句话理解
argparse 是 Python 标准库中用于解析命令行参数(CLI 参数)的模块
换句话说:
-
你在终端里敲:
python main.py --host 127.0.0.1 --port 8080 --debug -
argparse 负责把这些参数:
-
校验是否合法
-
转成 Python 类型
-
自动生成
--help帮助信息 -
以“对象属性”的形式给你用
-
3️⃣ argparse vs 其他方案
-
sys.argv:原始但繁琐,需要手动解析
-
getopt:类似C的getopt(),功能有限
-
optparse:已弃用,被argparse取代
-
click/docopt:第三方库,各有特色
sys.argv 是这样用的:
import sys
print(sys.argv)
| 问题 | 说明 |
|---|---|
| 无类型校验 | 全是字符串 |
| 参数顺序敏感 | 易出错 |
| 无自动 help | 要自己写 |
| 错误提示不友好 | 用户体验差 |
| 维护困难 | 参数多了就是灾难 |
👉 argparse = 标准化 + 自动化 + 用户友好
二、argparse 的基本工作流程
argparse 的典型使用流程只有 4 步:
1. 创建解析器 (ArgumentParser)
2. 定义参数 (add_argument)
3. 解析参数 (parse_args)
4. 使用参数 (args.xxx)
示例
1️⃣ 最小可用示例(Hello argparse)
import argparse
# 1. 创建解析器
parser = argparse.ArgumentParser(description='描述')
# 2. 添加参数
parser.add_argument("--name", help="你的名字")
# 3. 解析参数
args = parser.parse_args()
# 4. 使用参数
print(f"Hello {args.name}")
运行:
python demo.py --name Alice
输出:
Hello Alice
自动支持:
python demo.py -h
2️⃣ 完整结构框架
import argparse
def main():
# 创建解析器(配置元信息)
parser = argparse.ArgumentParser(
prog='myprogram',
description='详细描述程序功能',
epilog='示例用法说明',
formatter_class=argparse.RawTextHelpFormatter
)
# 添加参数
# ... 参数定义
# 解析参数
args = parser.parse_args()
# 业务逻辑
if args.verbose:
print("详细模式开启")
return 0
if __name__ == '__main__':
exit(main())
三、ArgumentParser 详解
1️⃣ 创建解析器
parser = argparse.ArgumentParser(
prog='myapp', # 程序名(默认sys.argv[0])
usage='%(prog)s [options]', # 使用说明字符串
description='程序功能描述', # 帮助文本前的描述
epilog='附加说明文本', # 帮助文本后的说明
parents=[], # 继承父解析器的参数
formatter_class=argparse.HelpFormatter, # 帮助格式类
prefix_chars='-', # 可选参数前缀(默认'-')
fromfile_prefix_chars='@', # 从文件读取参数的前缀
argument_default=None, # 参数的默认默认值
conflict_handler='error', # 参数冲突处理策略
add_help=True, # 是否添加-h/--help
allow_abbrev=True # 是否允许长参数缩写
)
2️⃣ formatter_class(进阶)
parser = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter
)
用途:
- 保留换行
- 写多行说明(非常实用)
formatter_class选项:
-
argparse.RawDescriptionHelpFormatter:保持描述格式 -
argparse.RawTextHelpFormatter:保持所有文本格式 -
argparse.ArgumentDefaultsHelpFormatter:显示默认值 -
argparse.MetavarTypeHelpFormatter:使用类型名作为metavar
四、add_argument()方法
1️⃣ add_argument 在定义什么?
parser.add_argument(...)
👉 本质是在定义三件事:
-
用户在命令行怎么输入
-
程序内部怎么接收
-
非法输入如何被拦截
2️⃣ 参数名(name / flags)
参数在命令行里的名字(位置参数或可选参数)
(1)位置参数(Positional)
parser.add_argument("filename")
使用:
python main.py data.txt
特点:
| 特点 | 说明 |
|---|---|
| 必须提供 | 顺序不能乱 |
| 没有 -- 前缀 | 靠位置识别 |
| 常用于 | 文件名、子命令 |
(2)可选参数(Optional)
parser.add_argument("-p", "--port")
使用:
python app.py --port 8080 # 长参数(推荐)
python app.py -p 8080 # 短参数
特点:
| 特点 | 说明 |
|---|---|
| 非必须 | 可有默认值 |
| 以 - 或 -- 开头 | |
| 顺序无关 |
最终使用的是属性名:
args.port
3️⃣ help —— 给“人”看的说明
参数说明文本,显示在
-h / --help
parser.add_argument("--port", help="服务端口")
python app.py -h
--port PORT 服务监听端口
4️⃣ type(类型转换)
parser.add_argument("--port", type=int)
args.port == 8080 # int
支持:
| type | 说明 |
|---|---|
int |
整数 |
float |
浮点 |
str |
字符串 |
bool |
⚠️ 不推荐直接用 |
| 自定义函数 | 高级用法 |
错误示例会自动提示:
--port abc
内置类型:
# 基本类型转换
parser.add_argument('--num', type=int)
parser.add_argument('--price', type=float)
parser.add_argument('--name', type=str) # 默认,可省略
# 文件类型
parser.add_argument('--input', type=argparse.FileType('r'))
parser.add_argument('--output', type=argparse.FileType('w', encoding='utf-8'))
自定义类型函数:
def percentage(value):
"""验证百分比值(0-100)"""
try:
value = float(value)
except ValueError:
raise argparse.ArgumentTypeError(f"无效的数值: {value}")
if not 0 <= value <= 100:
raise argparse.ArgumentTypeError("百分比必须在0-100之间")
return value
def existing_file(path):
"""验证文件存在"""
if not os.path.isfile(path):
raise argparse.ArgumentTypeError(f"文件不存在: {path}")
return path
parser.add_argument('--progress', type=percentage)
parser.add_argument('--config', type=existing_file)
5️⃣ default —— 没写参数时的默认值
parser.add_argument("--port", default=8080, type=int)
特点:
- 参数没提供时使用
- help 中会显示(推荐)
6️⃣ required(强制参数)
parser.add_argument("--config", required=True)
效果:
error: the following arguments are required: --config
👉 仅对可选参数生效
7️⃣ action(行为模式)⭐
action 是在“决定参数如何存值”
parser.add_argument('--xxx', action=...)
👉 action 决定三件事:
- 这个参数是否需要值
- 出现在命令行后,args.xxx 会变成什么
- 出现一次 vs 多次,行为是否不同
基础动作:
# 1. action="store" - 存储参数值(默认) 存你输入的值,把命令行中提供的值,原样存下来
parser.add_argument('-p','--port') <==> parser.add_argument('-p','--port', action='store')
python app.py --port 8080 args.port == '8080' (默认是字符串,可配合 `type=int`)
# 2. action="store_true" / "store_false" - 布尔开关, 参数是否出现,决定 True / False
parser.add_argument("--debug", action="store_true")
parser.add_argument('--no-cache', action='store_false', dest='use_cache')
效果:
| 使用 | args.debug |
| ---- | ---------- |
| 未传 | False |
| 传了 | True |
👉 **布尔开关的正确写法**
# 3. action="store_const" —— 出现即存一个“固定值” 比 `store_true` 更灵活(不只是 True / False)
parser.add_argument('--log-debug',action='store_const',const='DEBUG',default='INFO')
python app.py # 不写 args.log_debug == 'INFO'
python app.py --log-debug # 写了 args.log_debug == 'DEBUG'
# 4. action="count" - 统计出现次数 参数出现几次,值就加几
parser.add_argument('-v', '--verbose', action='count', default=0)
# -v → 1, -vv → 2, -vvv → 3
适合日志等级:
if args.v >= 2:
...
# 5. action="append" - 多次出现时追加到列表
parser.add_argument("--tag", action="append")
命令行使用:
--tag a --tag b
结果:
args.tag == ["a", "b"]
常见组合(强烈推荐)
parser.add_argument('--env', action='append', choices=['dev', 'test', 'prod'])
# 6. action="append_const" - 追加常量值 参数不接收值,但每出现一次就 append 一个固定值
parser.add_argument('--dev', action='append_const', const='development')
parser.add_argument('--prod', action='append_const', const='production')
python app.py --dev --prod
args.dev == ['development']
args.prod == ['production']
特殊动作:
# help - 显示帮助
parser.add_argument('--help', action='help')
# version - 显示版本
parser.add_argument('--version', action='version', version='%(prog)s 1.0')
8️⃣ choices(可选值约束)
# 限制可选值
parser.add_argument(
"--env",
choices=["dev", "test", "prod"]
)
parser.add_argument('--size', choices=range(1, 11), type=int)
# 使用枚举
from enum import Enum
class LogLevel(Enum):
DEBUG = 0
INFO = 1
WARNING = 2
ERROR = 3
parser.add_argument('--log-level', type=LogLevel, choices=list(LogLevel))
非法值会自动报错。
9️⃣ nargs(参数个数控制)
| 值 | 含义 | 示例 |
|---|---|---|
N |
接收N个参数 | nargs=3 → cmd a b c |
? |
0或1个参数 | nargs='?' → cmd 或 cmd arg |
* |
0或多个参数 | nargs='*' → cmd 或 cmd a b |
+ |
1或多个参数 | nargs='+' → cmd a 或 cmd a b |
argparse.REMAINDER |
剩余所有参数 | 用于包装其他命令 |
示例:
# 接受3个坐标值
parser.add_argument('coords', nargs=3, type=float)
# 接受可变数量的文件
parser.add_argument('files', nargs='*')
# 必填的可变参数
parser.add_argument('inputs', nargs='+', help='至少一个输入')
🔟 metavar(help 显示名)
parser.add_argument("--port", metavar="PORT")
只影响 help 文档显示。
1️⃣1️⃣ 高级参数配置
参数组
# 创建参数组
group = parser.add_argument_group(title="mandatory arguments")
group.add_argument("-H", "--host",
help="define the host to request. To change the port just add ':portnumber' to this parameter")
group = parser.add_argument_group(title="authentication arguments")
group.add_argument("-u", "--username", help="the login user name")
group.add_argument("-p", "--password", help="the login password")
group.add_argument("-f", "--authfile", help="authentication file with user name and password")
group.add_argument("--sessionfile", help="define name of session file")
group.add_argument("--sessionfiledir", help="define directory where the plugin saves session files")
group.add_argument("--nosession", action='store_true',
help="Don't establish a persistent session and log out after check is finished")
互斥参数
# 创建互斥组
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--create', action='store_true')
group.add_argument('--delete', action='store_true')
group.add_argument('--update', action='store_true')
# 互斥组中也可包含带值的参数
exclusive = parser.add_mutually_exclusive_group()
exclusive.add_argument('--fast', action='store_true')
exclusive.add_argument('--slow', action='store_true')
exclusive.add_argument('--speed', type=int)
默认值和动态默认值
import datetime
def default_output_name():
return f"output_{datetime.datetime.now():%Y%m%d}.txt"
parser.add_argument(
'-o', '--output',
default=default_output_name, # 函数作为默认值
help=f"输出文件名(默认: {default_output_name()})"
)
参数别名和dest
parser.add_argument(
'-c', '--config', '--conf', # 多个别名
dest='config_file', # 存储的属性名
metavar='CONFIG', # 帮助中显示的参数值名称
help='配置文件路径'
)
# 使用: args.config_file
五、parse_args 解析结果
args = parser.parse_args()
- 返回
Namespace对象 - 类似:
args.host
args.port
args.debug
本质:
vars(args)
六、子命令系统
6.1 基本子命令
parser = argparse.ArgumentParser(prog='git')
subparsers = parser.add_subparsers(
title='可用命令',
description='有效子命令',
help='附加帮助',
dest='command', # 存储子命令名称的属性
required=True # Python 3.7+
)
# 创建子命令解析器
parser_init = subparsers.add_parser('init', help='初始化仓库')
parser_init.add_argument('--bare', action='store_true')
parser_clone = subparsers.add_parser('clone', help='克隆仓库')
parser_clone.add_argument('repository')
parser_clone.add_argument('--depth', type=int)
parser_commit = subparsers.add_parser('commit', help='提交更改')
parser_commit.add_argument('-m', '--message', required=True)
kubectl / systemctl 的设计方式
import argparse
def main():
parser = argparse.ArgumentParser(description="运维工具集")
subparsers = parser.add_subparsers(dest="cmd", required=True)
check = subparsers.add_parser("check")
check_sub = check.add_subparsers(dest="target", required=True)
cpu = check_sub.add_parser("cpu")
cpu.add_argument("--threshold", type=int, default=80)
disk = check_sub.add_parser("disk")
disk.add_argument("--mount", default="/")
disk.add_argument("--threshold", type=int, default=90)
restart = subparsers.add_parser("restart")
restart.add_argument("--service", required=True)
args = parser.parse_args()
print(args)
if __name__ == "__main__":
main()
6.2 子命令间的共享参数
# 父级共享参数
parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument('--verbose', action='store_true')
parent_parser.add_argument('--config', default='config.ini')
# 子命令继承父级参数
parser_init = subparsers.add_parser(
'init',
parents=[parent_parser], # 继承参数
add_help=False # 避免重复的--help
)
6.3 子命令的默认动作
def handle_init(args):
print(f"初始化仓库,bare={args.bare}")
def handle_clone(args):
print(f"克隆 {args.repository}")
# 设置子命令的处理函数
parser_init.set_defaults(func=handle_init)
parser_clone.set_defaults(func=handle_clone)
# 主程序中调用
args = parser.parse_args()
if hasattr(args, 'func'):
args.func(args)
七、高级特性与技巧
7.1 从文件读取参数
# 创建支持@文件前缀的解析器
parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
# 使用: python script.py @config.txt
# config.txt内容(每行一个参数):
# --verbose
# --input=file.txt
# --output=result.txt
7.2 参数验证
def validate_args(args):
"""自定义参数验证"""
errors = []
if args.start > args.end:
errors.append("起始值不能大于结束值")
if args.threads <= 0:
errors.append("线程数必须大于0")
if errors:
parser.error('\n'.join(errors))
# 解析后验证
args = parser.parse_args()
validate_args(args)
7.3 自定义错误处理
class CustomArgumentParser(argparse.ArgumentParser):
def error(self, message):
# 自定义错误格式
self.print_usage(sys.stderr)
sys.stderr.write(f"\n错误: {message}\n")
sys.stderr.write("使用 -h 查看帮助\n")
sys.exit(2)
7.4 参数命名空间扩展
class CustomNamespace(argparse.Namespace):
def __init__(self):
super().__init__()
self.computed_value = None
def compute(self):
self.computed_value = f"{self.input}_{self.output}"
parser = argparse.ArgumentParser()
# ... 添加参数
args = parser.parse_args(namespace=CustomNamespace())
args.compute()
print(args.computed_value)
7.5 环境变量支持
import os
class EnvDefault(argparse.Action):
"""从环境变量获取默认值的Action"""
def __init__(self, envvar, required=True, default=None, **kwargs):
if envvar in os.environ:
default = os.environ[envvar]
if required and default:
required = False
super().__init__(default=default, required=required, **kwargs)
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values)
parser.add_argument(
'--api-key',
action=EnvDefault,
envvar='API_KEY',
help='API密钥(可从环境变量API_KEY读取)'
)
八、实用示例:完整命令行程序
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Chk_H3C_UME Counter
import argparse
import requests
from requests.auth import HTTPBasicAuth
def parse_arguments():
parser = argparse.ArgumentParser(description="Send GET request")
parser.add_argument('-i', '--ip', required=True, help='The IP address of the target server')
parser.add_argument('-u', '--username', required=True, help='Username')
parser.add_argument('-p', '--password', required=True, help='Password')
parser.add_argument('-k', '--filter-key', help='filter value')
return parser.parse_args()
def get_chassis_info(ip, username, password):
url = f"http://{ip}/redfish/v1/Chassis/1/Drives"
try:
response = requests.get(url, auth=HTTPBasicAuth(username, password), timeout=10)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print("1")
return None
def get_preferred_path(ip, username, password):
chassis_data = get_chassis_info(ip, username, password)
if not chassis_data:
print("0")
return chassis_data.get("Members","")[0]['@odata.id']
def send_get_request(ip, username, password):
auto_path = get_preferred_path(ip, username, password)
if not auto_path:
return None
url = f"http://{ip}{auto_path}"
try:
response = requests.get(url, auth=HTTPBasicAuth(username, password), timeout=10)
response.raise_for_status()
return response
except requests.exceptions.RequestException as e:
print("1")
return None
def filter_content(response, filter_key):
if not response:
print("1")
return
if filter_key:
try:
data = response.json()
value = data.get(filter_key)
print(value if value is not None else "Null")
except ValueError:
print("1")
else:
print(response.text)
def main():
args = parse_arguments()
response = send_get_request(args.ip, args.username, args.password)
if response:
filter_content(response, args.filter_key)
else:
print("1")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
文件处理器 - 一个完整的argparse示例程序
"""
import argparse
import sys
import os
from typing import List, Optional
from pathlib import Path
class FileProcessor:
def __init__(self, args):
self.args = args
self.validate_arguments()
def validate_arguments(self):
"""验证参数逻辑一致性"""
if self.args.mode == 'merge' and len(self.args.input_files) < 2:
raise ValueError("合并模式需要至少两个输入文件")
if self.args.compression < 0 or self.args.compression > 9:
raise ValueError("压缩级别必须在0-9之间")
def run(self):
"""主处理逻辑"""
if self.args.verbose >= 1:
print(f"开始处理,模式: {self.args.mode}")
if self.args.mode == 'copy':
self.process_copy()
elif self.args.mode == 'merge':
self.process_merge()
elif self.args.mode == 'split':
self.process_split()
if self.args.verbose >= 1:
print("处理完成")
def create_parser() -> argparse.ArgumentParser:
"""创建并配置参数解析器"""
# 主解析器
parser = argparse.ArgumentParser(
prog='fileproc',
description='多功能文件处理器',
epilog='''
示例:
%(prog)s copy input.txt output.txt
%(prog)s merge file1.txt file2.txt --output merged.txt
%(prog)s split large.txt --size 1000 --output-dir parts/
''',
formatter_class=argparse.RawDescriptionHelpFormatter,
allow_abbrev=False # 禁用缩写,避免歧义
)
# 全局参数组
global_group = parser.add_argument_group('全局选项')
global_group.add_argument(
'-v', '--verbose',
action='count',
default=0,
help='详细输出级别(-v: 一般信息,-vv: 调试信息)'
)
global_group.add_argument(
'--log-level',
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
default='INFO',
help='日志级别'
)
global_group.add_argument(
'--config',
type=argparse.FileType('r'),
help='配置文件路径'
)
# 子命令
subparsers = parser.add_subparsers(
title='可用命令',
dest='mode',
required=True,
metavar='MODE'
)
# copy 命令
parser_copy = subparsers.add_parser(
'copy',
help='复制文件',
description='复制文件到新位置'
)
parser_copy.add_argument(
'input_files',
nargs=1,
metavar='INPUT',
help='输入文件'
)
parser_copy.add_argument(
'output_file',
metavar='OUTPUT',
help='输出文件'
)
parser_copy.add_argument(
'--force',
action='store_true',
help='覆盖已存在的文件'
)
# merge 命令
parser_merge = subparsers.add_parser(
'merge',
help='合并多个文件',
description='将多个文件合并为一个文件'
)
parser_merge.add_argument(
'input_files',
nargs='+',
metavar='INPUT',
help='输入文件(至少两个)'
)
parser_merge.add_argument(
'-o', '--output',
required=True,
help='输出文件路径'
)
parser_merge.add_argument(
'--separator',
default='\n',
help='文件间的分隔符(默认: 换行符)'
)
# split 命令
parser_split = subparsers.add_parser(
'split',
help='分割文件',
description='将大文件分割为多个小文件'
)
parser_split.add_argument(
'input_file',
metavar='INPUT',
help='输入文件'
)
parser_split_group = parser_split.add_mutually_exclusive_group(required=True)
parser_split_group.add_argument(
'--size',
type=int,
help='每个分割文件的大小(字节)'
)
parser_split_group.add_argument(
'--parts',
type=int,
help='分割成的文件数量'
)
parser_split.add_argument(
'--output-dir',
type=Path,
default=Path('.'),
help='输出目录(默认: 当前目录)'
)
# 输出选项组(多个子命令共享)
output_group = argparse.ArgumentParser(add_help=False)
output_group.add_argument(
'--encoding',
default='utf-8',
help='文件编码(默认: utf-8)'
)
output_group.add_argument(
'--compression',
type=int,
choices=range(0, 10),
default=0,
metavar='LEVEL',
help='压缩级别 0-9(0: 无压缩, 9: 最大压缩)'
)
# 将输出选项添加到需要的子命令
parser_copy.add_argument_group('输出选项')._group_actions.extend(
output_group._group_actions
)
parser_merge.add_argument_group('输出选项')._group_actions.extend(
output_group._group_actions
)
return parser
def main() -> int:
"""主入口函数"""
parser = create_parser()
try:
args = parser.parse_args()
# 根据详细级别调整日志
if args.verbose >= 2:
print("调试模式启用")
print(f"参数: {args}")
# 创建处理器并运行
processor = FileProcessor(args)
processor.run()
return 0
except argparse.ArgumentError as e:
print(f"参数错误: {e}", file=sys.stderr)
return 1
except Exception as e:
print(f"错误: {e}", file=sys.stderr)
if args.verbose >= 1: # 如果启用详细模式,打印堆栈跟踪
import traceback
traceback.print_exc()
return 2
if __name__ == '__main__':
sys.exit(main())
九、最佳实践与注意事项
9.1 设计建议
-
提供有意义的默认值:减少用户必须提供的参数
-
清晰的帮助文本:每个参数都应有help描述
-
合理的参数分组:使用add_argument_group()组织相关参数
-
一致的命名规范:使用小写和连字符(--output-file)
-
适当的必需性:尽量少用required=True,优先使用默认值
9.2 常见陷阱
# 错误:dest名称冲突
parser.add_argument('--input', dest='input')
parser.add_argument('--input-file', dest='input') # 冲突!
# 错误:互斥组中的默认值
group = parser.add_mutually_exclusive_group()
group.add_argument('--fast', action='store_true', default=True) # 无效!
# 正确:使用required参数控制
group = parser.add_mutually_exclusive_group(required=True)
9.3 测试建议
import argparse
from io import StringIO
def test_parser():
parser = create_parser()
# 测试帮助输出
help_output = StringIO()
parser.print_help(file=help_output)
assert "用法" in help_output.getvalue()
# 测试参数解析
test_args = ['copy', 'input.txt', 'output.txt', '--verbose']
args = parser.parse_args(test_args)
assert args.mode == 'copy'
assert args.verbose == 1
# 测试错误处理
try:
parser.parse_args(['invalid_command'])
except SystemExit:
pass # 预期退出
十、总结
argparse模块提供了强大而灵活的命令行参数解析功能。掌握其核心概念和高级特性可以帮助你:
-
创建用户友好的命令行界面
-
实现复杂的参数验证和依赖关系
-
构建具有子命令的复杂CLI工具
-
提供完整的帮助文档和错误提示
通过合理设计参数结构、提供清晰的帮助信息、实现健壮的参数验证,你可以创建出专业级的命令行应用程序。argparse作为Python标准库的一部分,具有良好的稳定性和向后兼容性,是开发命令行工具的首选方案。
对于更复杂的CLI需求,可以结合第三方库如click(更易用的装饰器语法)或typer(基于类型提示),但argparse仍然是理解命令行参数处理基础的重要工具。

浙公网安备 33010602011771号