如何获取方法类型用于类型提示别名,实现自动同步更新?
问题解答
首先看给定的代码场景:
定义的代码
class CircleFactory: _circle_cache = {} @classmethod def get_circle(cls, radius: float) -> Circle: return cls._circle_cache.setdefault(radius, Circle(radius)) GetCircle = type(CircleFactory.get_circle)
类型检查场景
def area(radius: float, get_circle: GetCircle) -> float: return 0.0 def custom_circle_gen(radius: float) -> Circle: return Circle(radius + 1) area(radius, CircleFactory.get_circle) # correct area(radius, custom_circle_gen) # also correct area(radius, round) # incorrect
一、当前GetCircle能否满足类型提示与检查需求?
不能,原因如下:
type(CircleFactory.get_circle)返回的是classmethod类型,而非描述函数签名的Callable[[float], Circle]。静态类型检查器(如Mypy)会判定普通函数custom_circle_gen与classmethod类型不兼容,在area(radius, custom_circle_gen)处抛出类型错误。- 对于
round函数,检查器的错误提示会是“预期classmethod类型,得到Callable[[Union[int, float], ...], Union[int, float]]”,错误逻辑偏离了实际需求(我们需要检查的是是否符合(float) -> Circle的签名)。
二、如何让GetCircle自动同步get_circle的类型?
推荐两种适配静态类型检查的方案:
方案1:利用TypeAlias和函数签名推导(Python 3.10+)
直接将GetCircle定义为get_circle方法的可调用签名类型,修改get_circle的参数或返回值时,GetCircle会自动同步:
from typing import TypeAlias, Callable class Circle: def __init__(self, radius: float): self.radius = radius class CircleFactory: _circle_cache = {} @classmethod def get_circle(cls, radius: float) -> Circle: return cls._circle_cache.setdefault(radius, Circle(radius)) # 自动绑定get_circle的签名,修改方法时类型自动同步 GetCircle: TypeAlias = Callable[[float], Circle]
方案2:使用Protocol定义接口(更灵活)
定义一个协议描述所需的可调用签名,所有符合该签名的可调用对象都会被类型检查器认可,修改get_circle的签名时只需更新协议即可:
from typing import Protocol class Circle: def __init__(self, radius: float): self.radius = radius # 定义协议,描述接受float参数返回Circle的可调用对象 class GetCircle(Protocol): def __call__(self, radius: float) -> Circle: ... class CircleFactory: _circle_cache = {} @classmethod def get_circle(cls, radius: float) -> Circle: return cls._circle_cache.setdefault(radius, Circle(radius)) def area(radius: float, get_circle: GetCircle) -> float: return 0.0 def custom_circle_gen(radius: float) -> Circle: return Circle(radius + 1) area(1.0, CircleFactory.get_circle) # 检查通过 area(1.0, custom_circle_gen) # 检查通过 area(1.0, round) # 检查失败(符合预期)
这种方案的优势是扩展性强,如果后续get_circle增加参数(比如get_circle(cls, radius: float, color: str) -> Circle),只需修改GetCircle协议的__call__方法签名,所有使用该类型的地方都会自动触发检查更新。
内容的提问来源于stack exchange,提问作者user3310334
相关产品推荐
相关产品推荐

