如何为遍历对象可迭代属性的生成器函数添加正确Python类型标注?
解决Python生成器函数的类型标注问题
要让mypy能检测出传入不存在属性的错误,你需要通过类型约束明确泛型T必须包含指定的可迭代属性。根据你的需求,有两种合适的方案:
方案1:使用属性访问器实现通用静态检查
将函数的属性名参数替换为属性访问器(Callable),让mypy能静态验证属性是否存在、类型是否匹配。这是通用场景下的最优解。
from typing import Iterator, TypeVar, Callable, Iterable T = TypeVar('T') S = TypeVar('S') def iter_attribute(objects: list[T], get_attribute: Callable[[T], Iterable[S]]) -> Iterator[S]: for obj in objects: yield from get_attribute(obj) # 示例使用 class A: def __init__(self): self.a = [1, 2, 3] objs = [A(), A()] # 正确调用:访问存在的属性`a` for item in iter_attribute(objs, lambda x: x.a): print(item) # 错误调用:mypy会直接报错,因为`A`类不存在`b`属性 # for item in iter_attribute(objs, lambda x: x.b): # print(item)
方案2:针对固定属性名用Protocol约束
如果你的生成器只处理某个固定名称的属性,可以定义Protocol来强制约束泛型T必须包含该属性,且属性是可迭代的S类型。
from typing import Protocol, Iterator, TypeVar, Iterable S = TypeVar('S', covariant=True) # 定义协议:要求类型包含名为`target_attr`的可迭代属性 class HasTargetAttr(Protocol[S]): target_attr: Iterable[S] T = TypeVar('T', bound=HasTargetAttr[S]) def iter_attribute(objects: list[T]) -> Iterator[S]: for obj in objects: yield from obj.target_attr # 示例使用 class ValidClass: def __init__(self): self.target_attr = [1, 2, 3] # 正确调用 for item in iter_attribute([ValidClass(), ValidClass()]): print(item) # 错误调用:mypy会报错,因为类没有`target_attr`属性 # class InvalidClass: # def __init__(self): # self.other_attr = [4,5,6] # for item in iter_attribute([InvalidClass(), InvalidClass()]): # print(item)
注意:字符串属性名的局限性
如果坚持用字符串作为属性名参数,由于字符串是动态值,mypy无法进行静态检查(除非用Literal限定允许的属性名,但会让函数失去通用性)。因此不推荐这种写法,除非是动态场景下的必要选择。
内容的提问来源于stack exchange,提问作者Ingo
相关产品推荐
相关产品推荐

