如何在Typer中实现参数继承/分组?是否支持类似argparse的互斥组?
在Typer中复用共享参数及实现互斥组功能
一、复用多个命令的共享参数
针对非子命令的多个命令共享参数的场景,有三种实用方式避免重复编写:
1. 定义共享参数变量
把重复的参数(如数据库连接参数)提前定义为typer.Option或typer.Argument实例,在每个命令函数中直接引用:
import typer # 定义共享的数据库参数 db_host = typer.Option("localhost", help="数据库主机地址") db_port = typer.Option(5432, help="数据库端口") db_user = typer.Option("admin", help="数据库用户名") app = typer.Typer() @app.command() def export_data( host: str = db_host, port: int = db_port, user: str = db_user, output_path: str = typer.Argument(..., help="导出文件路径") ): typer.echo(f"从{host}:{port}导出数据到{output_path}") @app.command() def import_data( host: str = db_host, port: int = db_port, user: str = db_user, input_path: str = typer.Argument(..., help="导入文件路径") ): typer.echo(f"从{input_path}导入数据到{host}:{port}") if __name__ == "__main__": app()
2. 装饰器封装共享参数
如果参数较多,可自定义装饰器将共享参数注入命令函数:
import typer from functools import wraps def with_db_params(func): @wraps(func) def wrapper( host: str = typer.Option("localhost", help="数据库主机地址"), port: int = typer.Option(5432, help="数据库端口"), user: str = typer.Option("admin", help="数据库用户名"), *args, **kwargs ): return func(host, port, user, *args, **kwargs) return wrapper app = typer.Typer() @app.command() @with_db_params def export_data(host: str, port: int, user: str, output_path: str): typer.echo(f"从{host}:{port}导出数据到{output_path}") @app.command() @with_db_params def import_data(host: str, port: int, user: str, input_path: str): typer.echo(f"从{input_path}导入数据到{host}:{port}") if __name__ == "__main__": app()
3. 依赖注入(Typer 0.3+支持)
通过Typer的依赖注入功能,定义获取共享参数的函数,在命令中用typer.Depends引用:
import typer from typing import Tuple def get_db_params( host: str = typer.Option("localhost", help="数据库主机地址"), port: int = typer.Option(5432, help="数据库端口"), user: str = typer.Option("admin", help="数据库用户名") ) -> Tuple[str, int, str]: return host, port, user app = typer.Typer() @app.command() def export_data( db_params: Tuple[str, int, str] = typer.Depends(get_db_params), output_path: str = typer.Argument(..., help="导出文件路径") ): host, port, user = db_params typer.echo(f"从{host}:{port}导出数据到{output_path}") @app.command() def import_data( db_params: Tuple[str, int, str] = typer.Depends(get_db_params), input_path: str = typer.Argument(..., help="导入文件路径") ): host, port, user = db_params typer.echo(f"从{input_path}导入数据到{host}:{port}") if __name__ == "__main__": app()
二、实现类似argparse的互斥组功能
Typer基于Click构建,完全支持参数互斥组,常用两种实现方式:
1. 通过context_settings定义互斥组
注册命令时,通过context_settings配置互斥组规则:
import typer app = typer.Typer() @app.command( context_settings={"mutually_exclusive_groups": [ {"name": "auth_mode", "options": ["token", "username"]} ]} ) def login( token: str = typer.Option(None, help="认证令牌"), username: str = typer.Option(None, help="用户名"), password: str = typer.Option(None, help="密码") ): if token: typer.echo(f"使用令牌{token}登录") elif username and password: typer.echo(f"使用用户名{username}登录") else: typer.echo("请提供令牌或用户名+密码") if __name__ == "__main__": app()
上述代码中,auth_mode组内的token和username参数互斥,无法同时传入。
2. 使用Click的MutuallyExclusiveOption
直接指定参数的cls为Click的MutuallyExclusiveOption,关联互斥参数:
import typer from click import MutuallyExclusiveOption app = typer.Typer() @app.command() def login( token: str = typer.Option( None, help="认证令牌", cls=MutuallyExclusiveOption, mutually_exclusive=["username"] ), username: str = typer.Option(None, help="用户名"), password: str = typer.Option(None, help="密码") ): if token: typer.echo(f"使用令牌{token}登录") elif username and password: typer.echo(f"使用用户名{username}登录") else: typer.echo("请提供令牌或用户名+密码") if __name__ == "__main__": app()
内容的提问来源于stack exchange,提问作者Tom Ron
相关产品推荐
相关产品推荐

