如何为Python类的多个方法强制统一函数签名并复用输入校验?
统一类方法签名与内置输入校验的实现方案
以下是3种常见的实现方案,可根据你的使用场景选择:
方案1:装饰器(最常用、改造成本最低)
用装饰器封装统一的参数默认值、校验逻辑,所有计算方法只需加装饰器即可,无需重复写校验代码。同时搭配ParamSpec可以保留方法的类型签名提示,IDE不会丢失补全能力。
示例代码
from functools import wraps from typing import Callable, ParamSpec, TypeVar import pandas as pd # Python 3.10以下版本可以从typing_extensions导入ParamSpec和TypeVar P = ParamSpec("P") R = TypeVar("R", bound=pd.Series) def calc_method(func: Callable[P, R]) -> Callable[P, R]: @wraps(func) def wrapper( self, relativistic: bool = False, tol: float = 0.001, verbose: int = 0, *args, **kwargs ) -> R: # 内置统一校验逻辑 if not 0 < tol < 1: raise ValueError("tol必须为0到1之间的浮点数") if verbose not in (0, 1, 2): raise ValueError("verbose仅支持取值0、1、2") # 校验通过后执行实际业务逻辑 return func(self, relativistic=relativistic, tol=tol, verbose=verbose, *args, **kwargs) return wrapper
业务方法使用方式
class PhysicsCalculator: @calc_method def calculate_mass(self, relativistic:bool=False, tol=0.001, verbose:int=0)->pd.Series: # 此处仅需写业务逻辑,无需重复校验 ans = pd.Series([...]) return ans @calc_method def calculate_energy(self, relativistic:bool=False, tol=0.001, verbose:int=0)->pd.Series: ans = pd.Series([...]) return ans
如果后续需要修改参数默认值、新增校验规则,仅需修改calc_method装饰器即可,所有方法同步生效。
方案2:模板方法(适合校验逻辑复杂、扩展需求多的场景)
把公共的参数处理、校验逻辑封装为类的内部公共方法,具体的计算方法仅需传入业务逻辑实现即可,完全不需要重复处理参数。
示例代码
import pandas as pd from typing import Callable class PhysicsCalculator: # 公共包装逻辑,统一处理参数、校验 def _calc_template(self, calc_logic: Callable[[bool, float, int], pd.Series], **kwargs) -> pd.Series: # 统一参数默认值 params = { "relativistic": kwargs.get("relativistic", False), "tol": kwargs.get("tol", 0.001), "verbose": kwargs.get("verbose", 0) } # 统一校验 if not 0 < params["tol"] < 1: raise ValueError("tol必须为0到1之间的浮点数") if params["verbose"] not in (0, 1, 2): raise ValueError("verbose仅支持取值0、1、2") # 执行业务逻辑 return calc_logic(**params) # 具体业务方法,仅需实现核心逻辑 def calculate_mass(self, **kwargs) -> pd.Series: def _logic(relativistic, tol, verbose): # 业务逻辑代码 return pd.Series([...]) return self._calc_template(_logic, **kwargs) def calculate_energy(self, **kwargs) -> pd.Series: def _logic(relativistic, tol, verbose): # 业务逻辑代码 return pd.Series([...]) return self._calc_template(_logic, **kwargs)
方案3:静态签名强制约束(需要配合类型检查工具)
如果需要从代码静态层面强制所有方法符合签名,避免运行时才发现参数写错,可以用Protocol定义方法签名协议,配合mypy等类型检查工具,不符合签名的代码会直接在检查阶段报错。
示例代码
from typing import Protocol import pandas as pd # 定义计算方法的签名协议 class CalcMethod(Protocol): def __call__(self, relativistic: bool = False, tol: float = 0.001, verbose: int = 0) -> pd.Series: ... class PhysicsCalculator: # 标注所有计算方法的类型为CalcMethod calculate_mass: CalcMethod calculate_energy: CalcMethod # 如果此处参数名、类型、返回值和协议不符,mypy会直接报错 def calculate_mass(self, relativistic:bool=False, tol=0.001, verbose:int=0)->pd.Series: ...
内容的提问来源于stack exchange,提问作者MYK
相关产品推荐
相关产品推荐

