Python 项目配置管理:从硬编码到 Pydantic Settings

2026-07-19 23:40:05 0点赞 0收藏 0评论

每个 Python 项目都要处理配置。从最简单的 settings.py 到复杂的多环境管理,配置管理的方式直接影响到项目的可维护性安全性

Python 项目配置管理:从硬编码到 Pydantic Settings

这篇文章不讲理论,直接从真实项目的配置演进出发,讲清楚配置管理到底怎么做才是对的。

一、初级阶段:硬编码

新手最常见的写法——配置直接写在代码里:

python

# config.py DB_HOST = "localhost" DB_PORT = 3306 DB_USER = "root" DB_PASS = "123456" API_KEY = "sk-xxxxxxxxxxxxx" REDIS_URL = "redis://localhost:6379/0"

在代码中直接引用:

python

from config import DB_HOST, DB_PASS def connect(): return f"连接 {DB_HOST} with {DB_PASS}"

问题

  • API Key、数据库密码等敏感信息明文存储在代码仓库中

  • 本地、测试、生产环境切换时需要手动改代码

  • 配置变更需要重新部署整个应用

  • 团队成员间共享配置文件容易造成冲突

二、中级阶段:环境变量

用环境变量替代硬编码:

python

import os DB_HOST = os.getenv("DB_HOST", "localhost") DB_PORT = int(os.getenv("DB_PORT", "3306")) DB_USER = os.getenv("DB_USER", "root") DB_PASS = os.getenv("DB_PASS") API_KEY = os.getenv("API_KEY") if not API_KEY: raise ValueError("API_KEY 环境变量未设置")

配合 .env 文件(不提交到 Git):

text

# .env DB_HOST=localhost DB_PORT=3306 DB_USER=root DB_PASS=123456 API_KEY=sk-xxxxxxxxx

加载 .env

bash

pip install python-dotenv

python

from dotenv import load_dotenv load_dotenv()

优势

  • 敏感信息脱离代码仓库

  • 不同环境用不同的 .env 文件(.env.dev.env.prod

  • 符合 12-Factor App 原则

仍存在的问题

  • 配置项一多,os.getenv() 散落各处

  • 类型转换需要手动处理(int()bool()list()

  • 缺少配置校验,漏设变量运行时才报错

  • 嵌套配置(如数据库、Redis 等多组相关配置)组织混乱

三、进阶阶段:Pydantic Settings

Pydantic Settings 将环境变量自动解析为类型安全的配置对象。这是目前 Python 项目配置管理的最佳实践

安装

bash

pip install pydantic-settings

基础用法

python

from pydantic_settings import BaseSettings from pydantic import Field class Settings(BaseSettings): """应用配置""" # 字段名默认对应环境变量名(大写) database_url: str = Field(alias="DATABASE_URL") api_key: str = Field(alias="API_KEY") debug: bool = Field(default=False, alias="DEBUG") max_connections: int = Field(default=100, alias="MAX_CONNECTIONS") allowed_hosts: list[str] = Field(default=[], alias="ALLOWED_HOSTS") class Config: env_file = ".env" env_file_encoding = "utf-8" # 使用时自动加载 settings = Settings() print(settings.database_url) print(settings.max_connections) # int 类型

嵌套配置

真实项目配置通常有层级结构:

python

from pydantic import BaseModel from pydantic_settings import BaseSettings class DatabaseSettings(BaseModel): host: str = "localhost" port: int = 3306 user: str = "root" password: str database: str = "app_db" @property def url(self) -> str: return f"mysql://{self.user}:{self.password}@{self.host}:{self.port}/{self.database}" class RedisSettings(BaseModel): host: str = "localhost" port: int = 6379 db: int = 0 @property def url(self) -> str: return f"redis://{self.host}:{self.port}/{self.db}" class Settings(BaseSettings): database: DatabaseSettings redis: RedisSettings debug: bool = False secret_key: str class Config: env_file = ".env" env_nested_delimiter = "__" # 嵌套分隔符

对应的 .env 文件:

text

# 嵌套配置用 __ 分隔 database__host=localhost database__port=3306 database__user=root database__password=123456 database__database=myapp redis__host=localhost redis__port=6379 debug=True secret_key=your-secret-key-here

使用:

python

settings = Settings() print(settings.database.url) # 自动拼接 print(settings.redis.url)

四、多环境管理

不同环境(开发、测试、生产)配置不同:

python

from pydantic_settings import BaseSettings from typing import Optional class Settings(BaseSettings): env: str = "development" debug: bool = False database_url: str api_key: str class Config: env_file = f".env.{env}" # 动态加载对应文件

或者用 pydantic-settingsEnvSettingsSource 实现更灵活的加载逻辑:

python

from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): env: str = "development" debug: bool = False database_url: str model_config = SettingsConfigDict( env_file=".env", env_file_encoding="utf-8", extra="ignore", ) # 通过环境变量 ENV=production 切换 # 加载顺序:.env → .env.{env} → 系统环境变量

五、配置校验:提前暴露问题

Pydantic 最强大的能力是配置校验

python

from pydantic import field_validator from pydantic_settings import BaseSettings class Settings(BaseSettings): database_url: str max_connections: int = 100 log_level: str = "INFO" rate_limit_per_minute: int = 60 @field_validator("max_connections") def validate_max_connections(cls, v): if v < 1: raise ValueError("max_connections 必须大于 0") if v > 500: raise ValueError("max_connections 不能超过 500") return v @field_validator("log_level") def validate_log_level(cls, v): allowed = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] if v.upper() not in allowed: raise ValueError(f"log_level 必须是 {allowed} 之一") return v.upper()

配置错误在应用启动时就会失败,而不是运行时才暴露。

六、结合不同配置源

有些配置来自文件,有些来自环境变量,有些来自 Vault 等密钥服务:

python

from pydantic_settings import BaseSettings import json class Settings(BaseSettings): database_url: str redis_url: str secret_key: str feature_flags: dict[str, bool] = {} class Config: # 可以从多个源加载 env_file = ".env" env_file_encoding = "utf-8" @classmethod def from_file(cls, path: str) -> "Settings": with open(path) as f: data = json.load(f) return cls(**data)

七、生产环境示例

一个完整的生产环境配置管理:

text

project/ ├── .env # 公共配置(提交) ├── .env.local # 本地覆盖(不提交) ├── .env.production # 生产环境(不提交,由 CI/CD 注入) ├── config/ │ ├── __init__.py │ ├── settings.py # Pydantic Settings 定义 │ └── logging.py # 日志配置 └── app/ └── main.py

settings.py

python

from functools import lru_cache from pydantic_settings import BaseSettings from pydantic import Field class Settings(BaseSettings): # 应用 app_name: str = "MyApp" debug: bool = Field(default=False) env: str = Field(default="development") # 数据库 database_url: str database_pool_size: int = Field(default=10) database_max_overflow: int = Field(default=20) # Redis redis_url: str # 外部服务 api_base_url: str api_timeout: int = Field(default=30) # 安全 secret_key: str jwt_expire_minutes: int = Field(default=60 * 24 * 7) class Config: env_file = ".env" env_file_encoding = "utf-8" case_sensitive = False @lru_cache def get_settings() -> Settings: return Settings()

main.py 中使用:

python

from config.settings import get_settings settings = get_settings() app = FastAPI(debug=settings.debug)

lru_cache 确保 Settings 只初始化一次(单例模式)。

八、踩坑记录

1. 环境变量名大小写

Pydantic Settings 默认区分大小写。如果环境变量是 DEBUG=true,字段名 debug 默认匹配不上。解决方法:

python

class Settings(BaseSettings): debug: bool = False class Config: case_sensitive = False # 不区分大小写

2. 嵌套配置分隔符

__ 作为嵌套分隔符时,注意不要和环境变量中的真实 __ 冲突。

python

# 如果环境变量本身就包含 __,需要自定义分隔符 class Settings(BaseSettings): database: DatabaseSettings class Config: env_nested_delimiter = "___" # 自定义为三个下划线

3. List 类型的环境变量

python

allowed_hosts: list[str] = [] # .env 文件写法(用逗号分隔) ALLOWED_HOSTS=localhost,127.0.0.1,example.com

九、总结

配置管理演进路径:

阶段方案优点缺点初级硬编码简单不安全、难维护中级os.getenv() + .env配置与代码分离类型不安全、组织混乱进阶Pydantic Settings类型安全、校验、自动补全需要学习成本

建议:新项目直接从 Pydantic Settings 起步。它已经是 Python 配置管理的事实标准,不仅解决了配置加载的问题,还提供了类型校验、嵌套配置、多源合并等能力。花 10 分钟配置好,后续维护省下的时间远不止这些。

本文为纯技术分享,不涉及任何品牌或产品。

展开 收起
0评论

当前文章无评论,是时候发表评论了
提示信息

取消
确认
评论举报

相关文章推荐

更多精彩文章
更多精彩文章
最新文章 热门文章
0
扫一下,分享更方便,购买更轻松