DataStruct Mixin 模式分析
概述
本文档分析了 FQuant DataStruct 模块中 Mixin 模式的使用情况,探讨了为什么某些类未使用 Mixin 模式的设计决策。
Mixin 模式简介
什么是 Mixin?
Mixin 是一种代码复用模式,通过多重继承来实现功能组合。Mixin 本质上是一个不单独使用的类,它提供一组可组合的方法。
核心特点
python
class MixinA:
def method_a(self):
print("A")
class MixinB:
def method_b(self):
print("B")
class Base:
def base_method(self):
print("Base")
# 组合使用
class MyClass(Base, MixinA, MixinB):
passMixin vs 普通继承
| 特性 | 普通继承 | Mixin |
|---|---|---|
| 用途 | "是" 关系 (IS-A) | "有" 关系 (HAS-A) |
| 单独使用 | ✅ 可以 | ❌ 不应该 |
| 多重继承 | 通常单继承 | 核心模式 |
| 职责 | 扩展父类 | 组合功能 |
DataStruct 中的 Mixin 实践
当前 Mixin 类
DataStruct 模块定义了 3 个 Mixin 类:
| Mixin 类 | 职责 |
|---|---|
QuotationIndicatorsMixin | 统计指标计算 (max, min, mean, amplitude 等) |
QuotationOperationsMixin | 数据筛选操作 (select_code, select_time 等) |
QuotationIOSMixin | 数据序列化导出 (to_csv, to_json 等) |
标准继承模式
python
class StockDayData(
QuotationDataStructBase, # 基类
QuotationIndicatorsMixin, # 指标计算
QuotationOperationsMixin, # 数据操作
QuotationIOSMixin # IO 导出
):
passMixin 约束
DataStruct 的 Mixin 实现遵循以下约束:
- 不直接实例化 - Mixin 类只能通过多重继承使用
- 不定义 init - 避免与基类构造函数冲突
- 放在基类之后 - 确保 MRO 正确
未使用 Mixin 的类分析
1. RealtimeBase (实时行情数据)
文件: realtime.py
类结构:
RealtimeBase- 实时行情基类StockRealtimeData- 股票实时数据FutureRealtimeData- 期货实时数据RealtimeSeries- 实时数据序列FutureTickData- 期货 Tick 数据
数据模型对比:
| 特性 | 批量数据 (StockDayData) | 实时数据 (RealtimeBase) |
|---|---|---|
| 数据结构 | pd.DataFrame (多行) | dict (单条) |
| 数据量 | 历史数据 (大量) | 实时推送 (单条) |
| 操作模式 | 批量处理 | 实时更新 |
| 时间维度 | 多时间点 | 当前时刻 |
| 核心接口 | 筛选、计算、导出 | 字段访问 |
字段差异:
python
# 实时数据特有的档口数据字段
@property
def bid1(self) -> Optional[float]: # 买一价
@property
def bid_vol1(self) -> Optional[int] # 买一量
@property
def ask1(self) -> Optional[float] # 卖一价
# ... bid2-5, ask2-5结论:这是合理的设计决策 ✅
理由:
- 字段不同 - 实时数据有档口数据,批量数据没有
- 操作不同 - 实时数据不需要筛选/重采样/指标计算
- 数据来源不同 - 批量数据来自数据库,实时数据来自 WebSocket
2. SecurityListData (证券列表数据)
文件: security_list.py
类职责:
python
class SecurityListData:
"""证券列表 - 基本信息管理"""
def get_stock(self, option=None) # 股票筛选
def get_index(self) # 指数获取
def get_etf(self) # ETF 获取
def filter_by_name(self, keyword) # 名称筛选
def filter_by_code(self, prefix) # 代码筛选数据特点:
- 字段简单:
sse, code, name - 操作独特:无通用的"指标计算"需求
- 不需要 Mixin 功能
结论:这是合理的设计决策 ✅
3. FinancialData (财务数据)
文件: financial.py
类职责:
python
class FinancialData:
"""财务数据 - 报表指标"""
def get_report_by_date(code, date) # 按日期查询
def get_key(code, date, key) # 按指标查询
def get_financial(code, start, end) # 按时间范围查询数据特点:
- 数据来源:财务报表(非行情)
- 操作模式:查询为主(非计算)
- 字段特殊:ROE, EPS, PE, PB, 资产负债率等
结论:这是合理的设计决策 ✅
4. 其他独立数据类
| 类名 | 文件 | 是否需要 Mixin | 理由 |
|---|---|---|---|
IndicatorData | indicator.py | ❌ 不需要 | 指标数据,自成体系 |
SeriesData | series.py | ❌ 不需要 | 序列封装,操作独特 |
StockTransactionData | transaction.py | ❌ 不需要 | 逐笔数据,操作特殊 |
IndexTransactionData | transaction.py | ❌ 不需要 | 指数逐笔,操作特殊 |
StockBlockData | block.py | ❌ 不需要 | 板块数据,结构独特 |
Mixin 使用决策矩阵
| 场景 | 推荐模式 | 示例 |
|---|---|---|
| 多数据类需要相同操作(筛选/计算/导出) | Mixin 模式 | StockDayData, IndexDayData |
| 数据字段不同但操作相似 | Mixin + 自定义 | - |
| 数据和操作都独特 | 独立类 | SecurityListData, FinancialData |
| 实时数据 vs 批量数据 | 分离设计 | RealtimeBase vs StockDayData |
总结
设计原则
- 职责分离 - Mixin 将不同职责分离到独立类
- 组合灵活 - 通过继承组合需要的特性
- 避免过度设计 - 不强行抽象,保持简单
设计决策总结
| 类 | 使用 Mixin | 理由 |
|---|---|---|
| 行情数据类 (Stock, Index, Future, Bond) | ✅ 是 | 操作通用,字段一致 |
| 实时数据类 (RealtimeBase) | ❌ 否 | 字段和操作都不同 |
| 证券列表 (SecurityListData) | ❌ 否 | 字段简单,操作独特 |
| 财务数据 (FinancialData) | ❌ 否 | 查询为主,无指标计算 |
| 指标数据 (IndicatorData) | ❌ 否 | 数据类型特殊 |
结论
DataStruct 模块的 Mixin 模式使用是合理的:
- 使用的类:需要共享指标计算、数据筛选、IO 导出等通用操作
- 未使用的类:数据字段或操作与其他类差异较大,独立实现更合适
这种设计遵循了"简单优于复杂"的原则,避免了过度抽象带来的维护成本。
附录:Mixin 优缺点
优点
- 代码复用
- 职责分离
- 灵活组合
- 符合开闭原则
缺点
- MRO 复杂性
- 属性冲突风险
- 调试困难(多层继承)
最佳实践
- Mixin 不定义
__init__ - Mixin 不直接实例化
- 明确继承顺序
- 避免属性冲突
相关文档
| 类型 | 文档 | 链接 |
|---|---|---|
| 模块首页 | DataStruct 首页 | README |
| 快速入门 | 快速入门 | quick-start |
| 核心概念 | 核心概念 | concepts |
| 架构设计 | 技术架构 | architecture |
| API 参考 | API 参考 | api |
| 案例库 | 案例库 | examples |