使用Mixin类简单重构设置模块 [复制链接]
发表于 2026-2-12 00:19:10 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
媒介

按照个人风俗,项目伊始我会按照如下布局构造项目设置,也就是设置文件放在conf/目次,单独写一个设置模块pkg/config.py去读取加载。有的小项目还好,没什么设置项。但有的项目要调用许多第三方的接口,设置文件写了一堆接口地点、认证方式等,设置模块也相应增长了几百行。看着这快上千行的设置模块,还是尽早改改比力好。
  1. conf/
  2.   app.toml
  3. pkg/
  4.   config.py
复制代码
有的项目会把设置打散,各个模块维护各自的设置,但对于使用单一设置模块的项目,除了设置模块,别的模块调用设置类单例的地方我都不想去碰,也懒得碰。这时间,使用Mixin类就比力符合。
在Python中,Mixin只是一种约定,语言层面没有显式支持,实际上就是python的多重继续。
代码的设置类

旧的设置模块pkg/config.py大概长如许,每个设置项都写成了动态属性。即便只是简单的取值,也大概会写许多。如果再加上校验,单个文件的内容就会许多了,鼠标滚轮翻快点估计就找不到哪对哪了。
  1. class Config:
  2.     def __init__(self) -> None:
  3.         self._config_file = Path(__file__).parent.parent.parent / "conf" / "config.toml"
  4.         self._config = self._load_config()
  5.     def _load_config(self) -> Dict[str, Any]:
  6.         if not self._config_file.exists():
  7.             raise FileNotFoundError(f"Configuration file {self._config_file} does not exist.")
  8.         with open(self._config_file, "rb") as f:
  9.             return tomllib.load(f)
  10.     @property
  11.     def service_host(self) -> str:
  12.         return self._config.get("service").get("host", "127.0.0.1")
  13.     @property
  14.     def service_port(self) -> int:
  15.         return self._config.get("service").get("port", 8000)
复制代码
拆分

简单示例

如果设置的层级特别深,Mixin里写一长串.get().get()也挺碍眼的。可以写一个基类BaseMixin,在基类中界说一个递归读取设置的方法。
  1. class BaseMixin:
  2.     _config: Dict[str, Any]
  3.    
  4.     def _get_conf(self, *keys: str, default: Any = None) -> Any:
  5.         """递归获取配置"""
  6.         data = self._config
  7.         for k in keys:
  8.             if isinstance(data, dict):
  9.                 data = data.get(k)
  10.             else:
  11.                 return default
  12.         return data if data is not None else default
  13. class FeatureMixin(BaseMixin):
  14.     @property
  15.     def is_feature_enabled(self) -> bool:
  16.         return self._get_conf("module", "submodule", "enabled", default=False)
  17.         
  18. from typing import Any, Dict
  19. class ServiceMixin(BaseMixin):
  20.     """处理 Service 相关的配置项"""
  21.     @property
  22.     def service_host(self) -> str:
  23.         return self._get_conf("service", "host", default="127.0.0.1")
  24.     @property
  25.     def service_port(self) -> int:
  26.         return self._get_conf("service", "port", default=8000)
  27. class DatabaseMixin(BaseMixin):
  28.     """处理 Database 相关的配置项"""
  29.     @property
  30.     def db_url(self) -> str:
  31.         return self._get_conf("database", "url", default="sqlite:///./test.db")
复制代码
组合成终极的Config类
  1. import tomllib
  2. from pathlib import Path
  3. class Config(ServiceMixin, DatabaseMixin):
  4.     """
  5.     最终的聚合类。继承了所有 Mixin,因此它拥有了所有定义好的 @property。
  6.     """
  7.     def __init__(self) -> None:
  8.         self._config_file = Path(__file__).parent.parent.parent / "conf" / "config.toml"
  9.         self._config = self._load_config()
  10.     def _load_config(self) -> Dict[str, Any]:
  11.         if not self._config_file.exists():
  12.             raise FileNotFoundError(f"Configuration file {self._config_file} does not exist.")
  13.         with open(self._config_file, "rb") as f:
  14.             return tomllib.load(f)
  15. # --- 调用端代码完全不需要修改 ---
  16. config = Config()
  17. print(config.service_host)  # 来源于 ServiceMixin
  18. print(config.db_url)        # 来源于 DatabaseMixin
复制代码
如上改造后,调用方依然使用config.db_url 如许的方式来使用,不消管设置模块怎样改动。以后如果再想新增设置,比如Redis的毗连设置,只必要新增一个RedisMixin类,并加到Config的继续列表里即可。
中心层聚合

当设置的Mixin类越来越多,Config类会有一溜排的Mixin类要继续,看着有颔首重脚轻。这时可以按逻辑范畴先举行聚合。
比如,数据库相干的先聚合成DBMixins(这种中心层聚合的Mixin类,保举定名后缀为Mixins)
  1. # pkg/config/mixins/db.py
  2. class PostgresMixin(BaseMixin):
  3.     @property
  4.     def pg_host(self) -> str:
  5.         pass
  6.         
  7.     @property
  8.     def pg_port(self) -> int:
  9.         pass
  10.         
  11. class RedisMixin(BaseMixin):
  12.     @property
  13.     def redis_host(self) -> str:
  14.         pass
  15.         
  16.     @property
  17.     def redis_port(self) -> int:
  18.         pass
  19.         
  20. class DBMixins(PostgresMixin, RedisMixin):
  21.     pass
复制代码
在Config类中组装
  1. # pkg/config/config.py
  2. from pkg.config.mixins.db import DBMixins
  3. class Config(DBMixins):
  4.     pass
复制代码
终极目次布局如下:
  1. conf/
  2.   config.toml
  3. pkg/
  4.   config/
  5.     __init__.py  # 在此创建 Config 类的单例
  6.     config.py    # 其中只有 Config 类
  7.     mixins/
  8.       __init__.py  # 定义 BaseMixin 基类
  9.       db.py
  10.       third_parties.py
复制代码
增补

对于新项目,可以试试把设置全部放到环境变量,各个模块实现各自的设置模块。长处就是找起来方便,而且从环境变量中读取设置也不消耗心文件读取的壅闭标题。现在许多运行在k8s上的服务喜好用这种读取环境变量的设置方式。缺点大概就是维护起来不太方便,毕竟设置被打散了,交接给别人的话,别人大概得到处找设置。

免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金.
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表