You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现类似SQLAlchemy ORM的Mypy兼容类属性类型注解

实现类SQLAlchemy ORM的类型安全属性声明

要搞定类SQLAlchemy的模型声明效果——类里用Column(String(30))定义属性,实例化后self.name能被识别为字符串,同时Mypy无报错、IDE类型提示正常,核心是用描述符协议配合泛型类型注解。下面是具体解决方案:

1. 实现带泛型标记的Column描述符

用泛型明确标记属性的实际类型,让类型检查工具能正确关联声明与实例访问的类型:

from typing import Generic, TypeVar, Any, Optional

T = TypeVar('T')

class Column(Generic[T]):
    def __init__(self, type_: Any, max_length: Optional[int] = None):
        self.type = type_
        self.max_length = max_length
        self._name: str = ""

    def __set_name__(self, owner: type, name: str):
        # 自动记录属性在类中的名称,不用手动指定
        self._name = name

    def __get__(self, instance: Any, owner: type) -> T:
        # 实例访问时返回实际值,类访问时返回描述符自身
        if instance is None:
            return self  # 类型检查工具会处理类访问的情况
        # 从实例的__dict__中取值,你也可以换成自己的存储逻辑
        return instance.__dict__.get(self._name, None)

    def __set__(self, instance: Any, value: T) -> None:
        # 可选:添加运行时类型/规则校验
        if self.max_length and isinstance(value, str) and len(value) > self.max_length:
            raise ValueError(f"值长度超过限制:{self.max_length}")
        instance.__dict__[self._name] = value

2. 定义模型基类(可选但推荐)

统一处理实例初始化逻辑,避免每个模型重复写初始化代码:

class Model:
    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            if hasattr(self, key):
                setattr(self, key, value)

3. 声明模型类并使用

现在可以像SQLAlchemy那样写模型,同时标注属性的实际类型,Mypy和IDE都会正确识别:

class User(Model):
    # 用Column[str]标记该属性实例化后是str类型
    name: str = Column[str](str, max_length=30)
    age: int = Column[int](int)

# 实例化测试
user = User(name="Alice", age=25)
print(user.name)  # IDE识别为str,Mypy无报错
user.age = 26     # 类型检查正常
user.name = 123   # Mypy会报错:Incompatible types in assignment (expression has type "int", variable has type "str")

4. Mypy配置优化

如果Mypy对描述符的类型识别还有问题,在项目根目录的mypy.ini中添加以下配置:

[mypy]
strict = True
enable_plugin = mypy.plugins.descriptors

关键细节

  • 泛型的作用:Column[T]是核心,它告诉类型检查工具,这个描述符在实例上访问时会返回T类型的值,解决了“声明时是Column类型,实例访问时是实际类型”的类型不兼容问题。
  • __set_name__方法:自动绑定属性名,和SQLAlchemy的逻辑一致,不用手动维护属性名和存储键的映射。
  • 运行时校验:在__set__方法里可以加自定义校验,保证运行时的数据合法性,和类型检查形成互补。

内容的提问来源于stack exchange,提问作者salvaom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 03:10:29