环境变量优先级高于config.py中硬编码配置但低于显式参数;实际生效顺序取决于配置库或自定义逻辑,关键在于读取时机——os.environ即时读取,config.py在模块导入时执行。

Python 项目里 config.py 和环境变量谁先生效?
环境变量优先级高于代码里硬编码的配置,但低于显式传入的参数。Python 本身不定义“配置加载顺序”,这完全取决于你用的配置库或自定义逻辑——os.environ 读取是即时的,而 config.py 是模块导入时执行的,所以关键在「谁先被读」。
常见错误:把敏感值(如 DB_PASSWORD)写死在 config.py 里,结果上线后发现环境变量没覆盖上,其实是 config.py 里直接赋值了 DB_PASSWORD = "dev123",根本没查 os.environ。
- 推荐做法:所有配置项都从
os.environ.get("KEY", default)获取,config.py只负责提供默认值和类型转换 - 避免在
config.py中直接使用os.environ["KEY"](会抛KeyError),一律用.get() - 如果用了
pydantic-settings,它默认按dotenv → 环境变量 → 默认值顺序合并,但需显式声明case_sensitive=False才能匹配db_url和DB_URL
用 python-dotenv 加载 .env 文件时,为什么本地跑正常、Docker 里不生效?
python-dotenv 不自动加载任何文件,必须显式调用 load_dotenv(),且只加载一次。Docker 容器启动时若没执行这行,.env 就等于不存在。
典型现象:print(os.environ.get("DEBUG")) 输出 None,但 .env 文件明明有 DEBUG=true。
立即学习“Python免费学习笔记(深入)”;
本软件完全免费,无任何bug。用户可放心使用,网关需单独注册,请联系软件作者。1、关于接口设置:721K 卡易智能点卡接口,易宝支付网银接口。2、关于账户功能:商户信息管理、玩家留言信箱、网关下载、资金管理。3、关于游戏管理:分区管理、添加分区、分组管理、比例模板、补发管理、获取代码。4、关于订单管理:订单查询、渠道管理、结算统计。5、关于数据统计:玩家排名、分区排名、渠道统计。6、程序是 .NE
- 确保在应用入口(如
main.py最顶部)就调用load_dotenv(),不要放在某个函数里延迟执行 - Docker 中注意路径:默认只加载当前工作目录下的
.env,如果CMD ["python", "app/main.py"],就要把.env拷贝到/app/下,或传参指定路径:load_dotenv(".env") -
load_dotenv(override=True)可让.env覆盖已存在的环境变量,但生产环境慎用——可能意外覆盖 K8s 注入的 secret
ConfigDict(Pydantic v2)和 BaseSettings(v1)的加载行为差异
v2 的 ConfigDict 不再自动扫描环境变量,必须配合 pydantic-settings 包;而 v1 的 BaseSettings 内置该能力。这不是版本升级就能平滑过渡的点。
错误示例:把 v1 的 class Settings(BaseSettings): DB_URL: str 直接改成 v2 的 BaseModel + ConfigDict,结果 DB_URL 始终是 None。
- v2 正确写法:
from pydantic_settings import BaseSettings,不是pydantic.BaseModel -
BaseSettings构造时默认启用环境变量读取,但字段名db_url对应环境变量DB_URL(全大写+下划线),不能靠alias改变映射规则 - 若需自定义前缀(如所有配置加
MYAPP_),用SettingsConfigDict(env_prefix="MYAPP_"),否则MYAPP_DB_URL不会被识别
多环境配置(dev/staging/prod)怎么避免手动改 ENV=prod?
靠一个 ENV 环境变量做 if-else 分支,容易漏掉某套配置、测试时切错环境。更可靠的方式是「配置文件分离 + 运行时加载」,而不是条件逻辑嵌套。
常见坑:在 config.py 里写 if os.environ["ENV"] == "prod": ... else: ...,结果 CI 流水线忘记注入 ENV,直接 fallback 到 dev 配置,线上连错数据库。
- 把不同环境配置拆成
config_dev.py、config_prod.py,主配置模块根据os.environ.get("CONFIG_MODULE")动态导入,失败则报错退出,不静默 fallback - 或者统一用
pydantic-settings的env_file参数:Settings(_env_file=".env.prod"),启动时由运维指定文件 - 禁止在配置模块里做网络请求、文件读写等副作用操作——配置加载阶段必须是纯函数式的,否则单元测试无法 mock
最常被忽略的一点:配置值的类型转换发生在加载之后。比如 DEBUG=true 在环境变量里是字符串,但你期望它是布尔值——pydantic 会自动转,而裸用 os.environ.get("DEBUG") 拿到的永远是字符串,得自己写 str.lower() in ("1", "true", "yes")。别省这一步,也别指望框架替你猜意图。









