文章目录

用 dotenv 管理环境变量

一句话结论
dotenv 不是“高级技巧”,而是现代工程的起点。

如果你在代码里见过下面这种写法,却不知道它到底解决了什么问题:

from dotenv import load_dotenv
import os

load_dotenv()
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")

那这篇文章就是为你写的。


一、为什么要用环境变量?

1. 初学者最常见的错误

OPENAI_API_KEY = "sk-xxxxxxx"

问题有三:

  1. 密钥泄露风险
  2. 无法区分开发 / 测试 / 生产环境
  3. 代码不可复用

一旦 push 到 GitHub,你的 key 就“裸奔”了


2. 正确的工程思想

代码不应该包含任何“环境相关”的信息

包括:

  • 密钥(API Key)
  • 数据库密码
  • 域名
  • 开关配置

这些都应该交给 环境变量


二、dotenv 是什么?

1. 定义

dotenv = 用文件的方式,管理环境变量

它解决的问题是:

本地开发时,没有方便的方式设置环境变量


2. .env 文件长什么样?

OPENAI_API_KEY=sk-xxxx
DB_HOST=localhost
DB_PORT=5432
DEBUG=true

特点:

  • 不是 Python 代码
  • 不需要引号
  • 一行一个变量

三、安装与基本用法

1. 安装

pip install python-dotenv

2. 最基础用法

from dotenv import load_dotenv
import os

load_dotenv()

api_key = os.getenv("OPENAI_API_KEY")
print(api_key)

👉 load_dotenv() 会:

  • 找到当前目录(或父目录)的 .env
  • 把变量加载进系统环境

四、dotenv 的核心机制(一定要懂)

1. dotenv 本质做了什么?

.env 文件
    ↓
load_dotenv()
    ↓
os.environ["OPENAI_API_KEY"]

👉 dotenv 只是“帮你设置环境变量”,不是配置中心


2. dotenv 加载优先级

默认行为:

  1. 系统环境变量
  2. .env 文件(不会覆盖已有环境变量)
load_dotenv(override=True)  # 强制覆盖

五、dotenv + Python 项目标准结构

推荐目录结构

project/
├── app/
│   ├── main.py
│   └── config.py
├── .env
├── .gitignore
└── requirements.txt

config.py(推荐写法)

from dotenv import load_dotenv
import os

load_dotenv()

class Settings:
    OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
    DEBUG = os.getenv("DEBUG") == "true"

settings = Settings()

使用:

from app.config import settings

print(settings.OPENAI_API_KEY)

六、dotenv + FastAPI / AI 项目实战

1. AI 项目(OpenAI / LangChain)

OPENAI_API_KEY=sk-xxx
OPENAI_BASE_URL=https://api.openai.com/v1
load_dotenv()

client = OpenAI(
    api_key=os.getenv("OPENAI_API_KEY"),
    base_url=os.getenv("OPENAI_BASE_URL"),
)

2. FastAPI 中只加载一次(重要)

❌ 错误写法:

# 每个文件都 load_dotenv
load_dotenv()

✅ 正确写法:

# main.py
load_dotenv()

其他文件只用 os.getenv


七、dotenv + .gitignore(安全必做)

.gitignore 必须包含

.env

否则等于没用 dotenv。


八、常见坑(90% 初学者都会踩)

1️⃣ .env 不生效

检查顺序:

  1. 文件名是不是 .env
  2. 是否在项目根目录
  3. 是否调用了 load_dotenv()

2️⃣ 用了 os.environ[] 报错

os.environ["OPENAI_API_KEY"]  # KeyError

改用:

os.getenv("OPENAI_API_KEY")   # None

3️⃣ .env 写了引号

OPENAI_API_KEY="sk-xxx"

❌ 不推荐
dotenv 会把引号当成内容的一部分


九、dotenv 和生产环境的关系

很重要的一点

dotenv 只用于本地开发

生产环境:

  • Docker
  • k8s
  • 云平台

都应该用:

export OPENAI_API_KEY=xxx

dotenv 的作用是:

模拟生产环境变量


这个问题问得非常到位 👍
因为 dotenv 到这里就“该退场了”,而很多新手正是卡在「本地能跑,线上一团糟」。

我直接给你一套 生产环境“正确心智模型 + 可落地方案”,你照着用就行。


load_dotenv 加载其他文件

load_dotenv() 默认只加载当前目录或父目录里的 .env
但你可以显式指定任何文件路径、甚至加载多个文件。


二、load_dotenv 的本质原理(先理解这个)

load_dotenv() 实际做了什么?

  1. 找到一个 .env 文件

  2. 逐行读取:

    KEY=value
    
  3. 等价执行:

    os.environ["KEY"] = "value"
    

👉 它只是帮你往 os.environ 里塞值


三、最基础用法(你已经会的)

from dotenv import load_dotenv

load_dotenv()

等价于:

  • 查找 .env
  • 加载成功就结束
  • 找不到也不会报错

四、加载“别的环境变量文件”的正确方式 ⭐

✅ 方法 1:显式指定文件(最常用)

from dotenv import load_dotenv

load_dotenv(".env.dev")

或绝对路径:

load_dotenv("/Users/zhangyu/project/.env.prod")

✅ 方法 2:根据环境动态加载(推荐)

import os
from dotenv import load_dotenv

env = os.getenv("ENV", "development")

if env == "development":
    load_dotenv(".env.dev")
elif env == "test":
    load_dotenv(".env.test")

对应文件

# .env.dev
OPENAI_API_KEY=dev-key
DEBUG=true
# .env.test
OPENAI_API_KEY=test-key
DEBUG=false

五、加载多个 env 文件(覆盖规则很重要)

load_dotenv(".env.base")
load_dotenv(".env.local", override=True)

覆盖规则

参数 说明
override=False(默认) 已存在的不覆盖
override=True 后面的覆盖前面的

👉 后加载的优先级更高


六、你很可能会踩的 3 个坑(提前告诉你)


❌ 坑 1:变量已经存在,load_dotenv 没效果

export DEBUG=false
load_dotenv(".env.dev")

👉 默认不会覆盖

✅ 解决:

load_dotenv(".env.dev", override=True)

❌ 坑 2:文件路径不对

load_dotenv(".env.dev")

但你是从别的目录启动:

uvicorn app.main:app

👉 当前工作目录不是项目根

✅ 推荐写法:

from pathlib import Path
from dotenv import load_dotenv

BASE_DIR = Path(__file__).resolve().parent
load_dotenv(BASE_DIR / ".env.dev")

❌ 坑 3:以为 dotenv 能“读取系统变量”

❌ 错误理解:

“load_dotenv 能加载服务器已有环境变量”

👉 不行
只读文件,不读系统

系统变量本来就在 os.environ


七、dotenv 和系统环境变量的优先级(非常重要)

来源 优先级
代码里 os.environ["X"] 最高
系统 / Docker / 云平台
dotenv

👉 dotenv 永远不应该覆盖生产环境


八、推荐写法(结合你实际)

目录结构

langchain-fastapi-demo/
├─ app.py
├─ agent_demo.py
├─ .env.dev
├─ .env.example

app.py(推荐)

import os
from pathlib import Path
from dotenv import load_dotenv

BASE_DIR = Path(__file__).parent

ENV = os.getenv("ENV", "development")

if ENV == "development":
    load_dotenv(BASE_DIR / ".env.dev")
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐