用Typer代替Argparse
Contents
最近从Argparse和python-fire迁移到Typer,Typer在某些方面比Argparse更抽象。这篇文章总结了Typer的核心概念和使用技巧。
Typer和Argparse核心差异 #
Typer的核心理念是函数签名即CLI接口,大部分情况下只需写普通Python函数,Typer会自动处理CLI部分。
Argparse思维模式 #
python
# argparse: 显式定义每个参数
parser = argparse.ArgumentParser()
parser.add_argument('--name', type=str, help='Your name')
parser.add_argument('--count', type=int, default=1)
args = parser.parse_args()Typer思维模式 #
python
# typer: 从函数签名自动推导
import typer
def main(name: str, count: int = 1):
print(f"Hello {name}! " * count)
if __name__ == "__main__":
typer.run(main)快速精通Typer #
一个最简单例子 #
python
import typer
# 最简单的 CLI
def main(name: str):
print(f"Hello {name}")
typer.run(main)理解参数类型映射 #
python
# Typer 自动转换类型
def main(
name: str, # 必需参数
age: int = 18, # 可选参数,默认值
verbose: bool = False, # --verbose 或 --no-verbose
items: list[str] = None # 可多次使用 --items a --items b
):
passOption和Argument #
python
from typer import Argument, Option
def main(
# Argument: 位置参数(不需要 --)
filename: str = Argument(..., help="Input file"),
# Option: 选项参数(需要 --)
output: str = Option("output.txt", "--output", "-o", help="Output file")
):
pass子命令模式 #
python
app = typer.Typer()
@app.command()
def add(x: int, y: int):
"""Add two numbers"""
print(x + y)
@app.command()
def multiply(x: int, y: int):
"""Multiply two numbers"""
print(x * y)
if __name__ == "__main__":
app()从Argparse迁移到Typer #
Argparse版本 #
python
parser = argparse.ArgumentParser()
parser.add_argument('input_file')
parser.add_argument('--output', '-o', default='output.txt')
parser.add_argument('--verbose', '-v', action='store_true')
parser.add_argument('--count', type=int, default=1)Typer等效版本 #
python
def main(
input_file: str = typer.Argument(...),
output: str = typer.Option('output.txt', '--output', '-o'),
verbose: bool = typer.Option(False, '--verbose', '-v'),
count: int = typer.Option(1, '--count')
):
pass常见问题和解决方案 #
省略号 … 的含义
#
python
# ... 表示必需参数(无默认值)
name: str = typer.Argument(...) # 必需
name: str = typer.Argument("default") # 可选,有默认值bool类型的特殊处理 #
python
# bool 会自动创建 --flag/--no-flag
def main(verbose: bool = False):
# 使用: --verbose 或 --no-verbose
pass回调函数和验证 #
python
def validate_age(value: int):
if value < 0:
raise typer.BadParameter("Age must be positive")
return value
def main(age: int = typer.Option(..., callback=validate_age)):
pass额外开销 #
Typer在启动时有额外的开销,主要来自:
类型注解的运行时解析
Click框架的装饰器链
Rich库的初始化(如果使用)
对于简单脚本,argparse的启动速度更快。但对于复杂应用,Typer的开销可以忽略不计。