何时需要定义自定义类双下划线属性(dunder class attributes)?
我注意到Pydantic、SQLAlchemy这类框架里大量使用自定义双下划线类属性——比如Pydantic BaseModel里的__pydantic_core_schema__、__pydantic_validator__,还有SQLAlchemy要求用户定义的__tablename__。这类属性和普通单下划线的私有属性不同,主要适用这些场景:
框架内部元数据的专属存储
框架运行时需要在类上存储一些核心运行数据,比如Pydantic的__pydantic_core_schema__存的是模型的验证/序列化规则,__pydantic_validator__存生成的验证器实例。这些数据是框架逻辑的核心依赖,不能让用户的业务代码随意修改,用带框架标识的双下划线命名,既能和用户自定义属性划清界限,也能明确这些属性的归属。用户与框架的约定式配置入口
框架会通过约定的双下划线属性让用户提供配置,这相当于用户和框架之间的“约定接口”。比如SQLAlchemy里你需要定义__tablename__来指定模型对应的数据表名;还有Django模型里的__verbose_name__,用来设置模型的人性化显示名称。这类属性是框架明确对外开放的配置点,用双下划线命名能避免和用户自己的业务字段重名。彻底避免命名冲突的隔离手段
框架要给类添加属性,但又怕和用户可能定义的属性撞名——比如用户在Pydantic模型里可能定义core_schema作为业务字段,但框架的__pydantic_core_schema__就完全不会冲突。双下划线开头的命名(尤其是带框架前缀的)相当于给框架属性加了专属命名空间,不管用户怎么定义业务字段,都不会干扰到框架的内部属性。标记类的特殊状态或类型
框架用这类属性来标记类的特殊特性,比如Pydantic的__pydantic_root_model__用来标记这个模型是根模型(专门处理单一字段的序列化),__pydantic_complete__标记模型是否完成初始化。这些属性是框架内部判断类特性的标志,用双下划线命名能降低用户误修改的概率,保证框架逻辑的稳定性。
内容的提问来源于stack exchange,提问作者Yuri Bakurov

