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

如何为多Mixin应用ABC或Protocol解决类型提示与Lint问题

解决DRF ModelSerializer扩展的类型提示与Lint问题

核心思路

结合ABC(强制实现校验)和Protocol(类型提示)的优势,避免重复定义:

  • 用ABC确保子类在定义阶段就暴露未实现的属性/方法
  • 用Protocol整合所有必要接口(包括ABC要求、ModelSerializer方法、其他Mixin方法),给Mixin提供完整类型提示
  • 统一类型注解,消除跨Mixin/父类的Lint错误

修正后的代码

from abc import ABC, abstractmethod
from typing import Protocol, type
from dataclasses import dataclass
from rest_framework.serializers import ModelSerializer


@dataclass
class Constraints:
    width: int
    height: int

# ABC:强制子类必须实现的属性与方法
class ThingsToImplement(ABC):
    my_number: int
    constraints: Constraints

    @abstractmethod
    def get_da_name(self, s: str) -> str:
        pass

# Protocol:整合ABC要求 + ModelSerializer核心方法 + 跨Mixin方法
class SerializerProtocol(Protocol, ThingsToImplement):
    # 继承ABC的所有约束
    # 添加ModelSerializer的核心成员
    instance: object
    def to_representation(self, instance) -> dict: ...
    # 添加其他Mixin的方法,供类型识别
    def get_number(self) -> int: ...
    def get_foo(self) -> str: ...

class ConstraintsMixin:
    def __init_subclass__(cls, **kwargs):
        if not hasattr(cls, 'constraints'):
            raise NotImplementedError('请添加constraints属性')

    @classmethod
    def print_constraints(cls: type[SerializerProtocol]) -> None:
        print(cls.constraints.width, cls.constraints.height)

class FooMixin:
    def get_foo(self: SerializerProtocol) -> str:
        return self.get_da_name("test") if self.constraints.width > 123 else 'Too small to be named'

    def get_bar(self: SerializerProtocol) -> str:
        # 可正常识别ModelSerializer的to_representation和instance
        return str(self.to_representation(self.instance))

    def from_another(self: SerializerProtocol) -> str:
        return f"from {self.get_number()}"

class AnotherMixin:
    def get_number(self: SerializerProtocol) -> int:
        return self.my_number

    def from_foo(self: SerializerProtocol) -> str:
        return "from " + self.get_foo()

# 调整基类顺序,解决元类冲突(ModelSerializer放最后,DRF元类优先级更高)
class AbstractSerializer(ConstraintsMixin, FooMixin, AnotherMixin, ThingsToImplement, ModelSerializer):
    def implemented_method_1(self):
        return 1

    def implemented_method_2(self):
        return 2

    # ...
    def implemented_method_30(self):
        return 30

class MySerializer(AbstractSerializer):
    my_number = 7
    constraints = Constraints(1, 2)

    def get_da_name(self, s: str) -> str:
        # 修复笔误:number改为my_number
        self.my_number += 1
        return 'hi ' + s

关键调整说明

  1. Protocol继承ABC:让SerializerProtocol自动包含ThingsToImplement的所有约束,避免重复定义,同时补充ModelSerializer和其他Mixin的方法,给Lint提供完整类型信息
  2. 统一类型注解:所有Mixin方法都用self: SerializerProtocol,Lint可识别跨Mixin方法(如get_number、get_foo)和ModelSerializer成员(to_representation、instance)
  3. 调整基类顺序:将ModelSerializer放在基类列表最后,解决DRF元类与ABC元类的冲突问题
  4. 修复代码笔误:MySerializer中self.number改为self.my_number,消除不存在的属性错误

方案优势

  • ABC实现定义阶段校验:子类未实现要求的属性/方法时,定义类时直接报错,无需等到运行时
  • Protocol提供完整类型提示:消除所有跨类成员的Lint警告
  • 两者结合既满足提前校验需求,又避免重复代码,兼顾类型安全与开发效率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 00:31:38