基于Python实现API与DB字段映射类的优化方案问询
数据库与API数据映射器的优化实现方案
需求描述
希望实现一个数据库字段与API响应字段的映射器,预期代码结构与行为如下:
class IData: a: str = ('db_a', 'type_a') b: str = ('db_b', 'type_b') c: str = ('db_c', 'type_c') class MyData(IData): a = 'api_a' b = 'api_b' c = 'api_c' m = MyData() # 或 `m = MyData` m.a.db_name -> 'db_a' m.a.db_type -> 'type_a' m.a.api_name -> 'api_a' m.get_db_names() -> ['db_a', 'db_b', 'db_c'] m.get_db_types() -> ['type_a', 'type_b', 'type_c'] m.get_api_names() -> ['api_a', 'api_b', 'api_c'] # 若MyData中未赋值则为None
同时要求:实例化MyData时,若某个字段的api_name未设置,需抛出异常。
现有实现
当前已完成的实现代码如下:
class _Field: def __init__(self, db_name, db_type, api_name=None): self.db_name = db_name self.db_type = db_type self.api_name = api_name class _base: def get_db_names(self): return [self.__dict__[i].db_name for i in self.__dict__ if not i.startswith("_")] def get_db_types(self): return [self.__dict__[i].db_type for i in self.__dict__ if not i.startswith("_")] def get_api_names(self): return [self.__dict__[i].api_name for i in self.__dict__ if not i.startswith("_")] class IData(_base): def __init__(self): self.a = _Field("db_a", "type_a") self.b = _Field("db_b", "type_b") self.c = _Field("db_c", "type_c") class MyData(IData): def __init__(self): super().__init__() self.a.api_name = "api_a" self.b.api_name = "api_b" self.c.api_name = "api_c"
基于dataclasses与元类的优化方案
以下是更简洁、健壮的实现,利用dataclasses简化字段定义,通过元类自动处理属性映射与校验逻辑:
完整代码
from dataclasses import dataclass from typing import List, Tuple @dataclass class Field: db_name: str db_type: str api_name: str = None class DataMapperMeta(type): def __new__(cls, name, bases, attrs): # 收集基类中已定义的Field实例 inherited_fields = {} for base in bases: if isinstance(base, DataMapperMeta): inherited_fields.update( {k: v for k, v in base.__dict__.items() if isinstance(v, Field)} ) # 处理当前类的属性 for attr_name, attr_value in attrs.items(): if attr_name.startswith('_'): continue # 基类中用元组定义(db_name, db_type) if isinstance(attr_value, tuple) and len(attr_value) == 2: inherited_fields[attr_name] = Field(*attr_value) # 子类中用字符串设置api_name elif isinstance(attr_value, str): if attr_name not in inherited_fields: raise ValueError(f"字段 {attr_name} 未在基类中定义") inherited_fields[attr_name].api_name = attr_value # 实现映射器的工具方法 def get_db_names(self) -> List[str]: return [f.db_name for f in inherited_fields.values()] def get_db_types(self) -> List[str]: return [f.db_type for f in inherited_fields.values()] def get_api_names(self) -> List[str]: return [f.api_name for f in inherited_fields.values()] # 更新类属性与方法 attrs.update({ **inherited_fields, "get_db_names": get_db_names, "get_db_types": get_db_types, "get_api_names": get_api_names }) # 创建类并添加实例化校验逻辑 new_cls = super().__new__(cls, name, bases, attrs) original_init = new_cls.__init__ def __init__(self, *args, **kwargs): original_init(self, *args, **kwargs) # 检查所有字段是否已设置api_name missing_fields = [ field_name for field_name, field in inherited_fields.items() if field.api_name is None ] if missing_fields: raise ValueError(f"以下字段未设置API名称: {', '.join(missing_fields)}") new_cls.__init__ = __init__ return new_cls # 定义基类与子类 class IData(metaclass=DataMapperMeta): a: str = ('db_a', 'type_a') b: str = ('db_b', 'type_b') c: str = ('db_c', 'type_c') class MyData(IData): a = 'api_a' b = 'api_b' c = 'api_c'
方案优势
- 代码简洁直观:完全符合你最初预期的类属性定义方式,无需在
__init__中手动创建字段实例 - 健壮性提升:元类统一处理属性映射逻辑,避免手动操作
__dict__带来的潜在问题 - 自动校验:实例化时自动检查所有字段的
api_name是否已设置,未设置则抛出明确异常 - 可维护性强:
Field类用dataclass定义,自动生成标准方法,减少样板代码
测试验证
# 正常实例化,符合预期行为 m = MyData() print(m.a.db_name) # 输出: 'db_a' print(m.a.db_type) # 输出: 'type_a' print(m.a.api_name) # 输出: 'api_a' print(m.get_db_names()) # 输出: ['db_a', 'db_b', 'db_c'] print(m.get_db_types()) # 输出: ['type_a', 'type_b', 'type_c'] print(m.get_api_names()) # 输出: ['api_a', 'api_b', 'api_c'] # 未设置全部api_name时抛出异常 class IncompleteData(IData): a = 'api_a' b = 'api_b' # c未设置api_name # i = IncompleteData() # 执行会抛出 ValueError: 以下字段未设置API名称: c
内容的提问来源于stack exchange,提问作者Anton
相关产品推荐
相关产品推荐

