如何改写代码使mypy兼容numbers.Number处理数值类对象序列?
问题描述
我希望使用mypy的静态鸭子类型特性编写一个函数,处理由numbers.Number定义的“类数值对象”序列,代码示例如下:
from numbers import Number from typing import Sequence def something_numerical(xs: Sequence[Number]) -> Number: ... print(something_numerical([1., 2., 3.]))
但调用该函数传入float或int列表时,mypy报错:
$ print(multiply([1., 2., 3.])) foo/foo.py:9: error: List item 0 has incompatible type "float"; expected "Number" foo/foo.py:9: error: List item 1 has incompatible type "float"; expected "Number" foo/foo.py:9: error: List item 2 has incompatible type "float"; expected "Number"
已知float并非numbers.Number的子类,但numbers模块的抽象基类(ABC)用于检查对象是否具备数值操作所需方法。请问如何改写代码,既能处理int、float、fractions.Fraction等类型,又能通过mypy的类型检查?
解决方案
方法1:显式声明数值类型的Union
直接定义包含所有目标数值类型的Union,配合Sequence使用,让mypy明确识别允许的输入类型:
from numbers import Number from typing import Sequence, Union import fractions # 列出所有需要支持的数值类型,包含Number以兼容自定义注册类型 NumericalType = Union[int, float, fractions.Fraction, Number] def something_numerical(xs: Sequence[NumericalType]) -> NumericalType: total = xs[0] for num in xs[1:]: total += num return total # 测试调用 print(something_numerical([1., 2., 3])) print(something_numerical([fractions.Fraction(1,2), fractions.Fraction(3,4)]))
方法2:使用带边界的TypeVar实现泛型
通过绑定到Number的TypeVar创建泛型函数,既支持所有Number兼容类型,又能保证输入输出类型一致:
from numbers import Number from typing import Sequence, TypeVar # 定义绑定到Number的类型变量,限制输入必须是Number兼容类型 T = TypeVar('T', bound=Number) def something_numerical(xs: Sequence[T]) -> T: total = xs[0] for num in xs[1:]: total += num return total # 测试调用 print(something_numerical([1., 2., 3.])) print(something_numerical([1, 2, 3]))
关键说明
numbers.Number是抽象基类,int、float等是它的虚拟子类(通过register方法注册,而非直接继承)。mypy默认不识别虚拟子类的类型兼容关系,因此需要通过上述方式明确类型约束,才能通过静态检查。两种方法中,泛型方式更灵活,适合需要支持自定义数值类型的场景;Union方式更直观,适合明确知道需要支持的具体类型的场景。
内容的提问来源于stack exchange,提问作者Alex Flint
相关产品推荐
相关产品推荐

