Python如何实现类自引用与互相引用且满足mypy类型校验
解决方案
方案1:使用__future__.annotations(Python 3.7+推荐)
Python 3.7引入的延迟类型注解特性,会将所有类内的类型注解自动转为字符串存储,等到整个模块加载完成后再解析类型,完美解决自引用、循环引用的NameError问题,同时mypy完全支持这种写法的类型校验。
注意你原代码中直接在类定义阶段实例化类本身作为类属性的写法存在无限递归实例化的逻辑问题:类A定义时需要创建A()实例,实例化A又需要先初始化A的类属性,会陷入死循环触发RecursionError,因此我们可以先将类属性赋值为None,标注为可选类型,后续再完成赋值,或者用类方法初始化类属性。
修正后代码示例:
#! /usr/bin/python3.10 from __future__ import annotations from typing import Optional class A: a: Optional[A] = None b: Optional[B] = None class B: a: Optional[A] = None b: Optional[B] = None # 类定义完成后再完成类属性赋值,避免无限递归 A.a = A() A.b = B() B.a = A() B.b = B()
这种写法mypy可以正常识别所有类型,不会丢失类型检查能力。
方案2:使用字符串形式类型注解(兼容所有Python版本)
如果需要兼容Python 3.6及更低版本,可以直接将未定义的类型名用引号包裹,手动标记为延迟解析的字符串类型,mypy同样可以正常识别:
#! /usr/bin/python3.10 from typing import Optional class A: a: Optional['A'] = None b: Optional['B'] = None class B: a: Optional['A'] = None b: Optional['B'] = None A.a = A() A.b = B() B.a = A() B.b = B()
补充说明
两种方案都可以同时满足自引用、类间循环引用的需求,且完全符合mypy的类型校验规则,不需要隐藏类型或者把属性移到类外导致类型丢失,也能保持代码的声明式写法规整性。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

