Python中如何将派生类列表作为基类列表传入函数?解决mypy错误
解决方法
先明确报错原因:Python的List属于不变类型,List[DerivedClass]不会被视为List[BaseClass]的子类——如果do_stuff函数里尝试往列表中添加仅符合BaseClass的元素,原列表的类型安全性会被破坏。而单个DerivedClass实例能隐式转为BaseClass,是因为单个实例的转换不存在这种风险。
下面是几种替代#type: ignore的解决方案:
1. 使用只读协变类型(推荐)
将函数参数的List[BaseClass]替换为Sequence[BaseClass]或Iterable[BaseClass]。这两种类型是只读的协变类型,mypy允许List[DerivedClass]隐式转换为它们——只读操作不会破坏列表的类型安全。
修改后的代码:
from typing import List, Sequence, TypedDict class BaseClass(TypedDict): base_field: str class DerivedClass(BaseClass): derived_field: str def do_stuff(data: Sequence[BaseClass]) -> None: pass foo: List[DerivedClass] = [{'base_field': 'foo', 'derived_field': 'bar'}] do_stuff(foo) # 不再报错
如果do_stuff不需要修改传入的列表,这是最优解,既安全又简洁。
2. 显式转换列表类型
如果必须用List作为参数类型,可以通过显式转换明确告知mypy类型是安全的:
方法2.1:列表推导式(更安全)
逐个将DerivedClass实例转为BaseClass,生成新列表:
from typing import List, TypedDict class BaseClass(TypedDict): base_field: str class DerivedClass(BaseClass): derived_field: str def do_stuff(data: List[BaseClass]) -> None: pass foo: List[DerivedClass] = [{'base_field': 'foo', 'derived_field': 'bar'}] # 显式转换每个元素 do_stuff([item for item in foo])
方法2.2:使用cast(需谨慎)
直接用cast告诉mypy列表的类型是List[BaseClass],但要确保函数不会修改列表(比如添加非DerivedClass的元素),否则会引发运行时错误:
from typing import List, TypedDict, cast class BaseClass(TypedDict): base_field: str class DerivedClass(BaseClass): derived_field: str def do_stuff(data: List[BaseClass]) -> None: pass foo: List[DerivedClass] = [{'base_field': 'foo', 'derived_field': 'bar'}] do_stuff(cast(List[BaseClass], foo))
3. 使用泛型函数
如果do_stuff需要支持任意BaseClass子类的列表,可以用TypeVar定义泛型:
from typing import List, TypedDict, TypeVar class BaseClass(TypedDict): base_field: str # 定义绑定BaseClass的泛型变量 BaseOrSubclass = TypeVar('BaseOrSubclass', bound=BaseClass) class DerivedClass(BaseClass): derived_field: str def do_stuff(data: List[BaseOrSubclass]) -> None: pass foo: List[DerivedClass] = [{'base_field': 'foo', 'derived_field': 'bar'}] do_stuff(foo) # 不再报错
这种方法让函数更灵活,能接受任何BaseClass子类的列表,同时保证类型安全。
内容的提问来源于stack exchange,提问作者Andreas Winther Moen
相关产品推荐
相关产品推荐

