文章目录
- [用 dotenv 管理环境变量](#用 dotenv 管理环境变量)
-
- 一、为什么要用环境变量?
-
- [1. 初学者最常见的错误](#1. 初学者最常见的错误)
- [2. 正确的工程思想](#2. 正确的工程思想)
- [二、dotenv 是什么?](#二、dotenv 是什么?)
-
- [1. 定义](#1. 定义)
- [2. `.env` 文件长什么样?](#2.
.env文件长什么样?)
- 三、安装与基本用法
-
- [1. 安装](#1. 安装)
- [2. 最基础用法](#2. 最基础用法)
- [四、dotenv 的核心机制(一定要懂)](#四、dotenv 的核心机制(一定要懂))
-
- [1. dotenv 本质做了什么?](#1. dotenv 本质做了什么?)
- [2. dotenv 加载优先级](#2. dotenv 加载优先级)
- [五、dotenv + Python 项目标准结构](#五、dotenv + Python 项目标准结构)
- [六、dotenv + FastAPI / AI 项目实战](#六、dotenv + FastAPI / AI 项目实战)
-
- [1. AI 项目(OpenAI / LangChain)](#1. AI 项目(OpenAI / LangChain))
- [2. FastAPI 中只加载一次(重要)](#2. FastAPI 中只加载一次(重要))
- [七、dotenv + .gitignore(安全必做)](#七、dotenv + .gitignore(安全必做))
-
- [.gitignore 必须包含](#.gitignore 必须包含)
- [八、常见坑(90% 初学者都会踩)](#八、常见坑(90% 初学者都会踩))
-
- [1️⃣ `.env` 不生效](#1️⃣
.env不生效) - [2️⃣ 用了 `os.environ[]` 报错](#2️⃣ 用了
os.environ[]报错) - [3️⃣ `.env` 写了引号](#3️⃣
.env写了引号)
- [1️⃣ `.env` 不生效](#1️⃣
- [九、dotenv 和生产环境的关系](#九、dotenv 和生产环境的关系)
- [`load_dotenv` 加载其他文件](#
load_dotenv加载其他文件) -
- [二、`load_dotenv` 的本质原理(先理解这个)](#二、
load_dotenv的本质原理(先理解这个)) -
- [`load_dotenv()` 实际做了什么?](#
load_dotenv()实际做了什么?)
- [`load_dotenv()` 实际做了什么?](#
- 三、最基础用法(你已经会的)
- [四、加载"别的环境变量文件"的正确方式 ⭐](#四、加载“别的环境变量文件”的正确方式 ⭐)
-
- [✅ 方法 1:显式指定文件(最常用)](#✅ 方法 1:显式指定文件(最常用))
- [✅ 方法 2:根据环境动态加载(推荐)](#✅ 方法 2:根据环境动态加载(推荐))
- 对应文件
- [五、加载多个 env 文件(覆盖规则很重要)](#五、加载多个 env 文件(覆盖规则很重要))
- [六、你很可能会踩的 3 个坑(提前告诉你)](#六、你很可能会踩的 3 个坑(提前告诉你))
-
- [❌ 坑 1:变量已经存在,load_dotenv 没效果](#❌ 坑 1:变量已经存在,load_dotenv 没效果)
- [❌ 坑 2:文件路径不对](#❌ 坑 2:文件路径不对)
- [❌ 坑 3:以为 dotenv 能"读取系统变量"](#❌ 坑 3:以为 dotenv 能“读取系统变量”)
- [七、dotenv 和系统环境变量的优先级(非常重要)](#七、dotenv 和系统环境变量的优先级(非常重要))
- 八、推荐写法(结合你实际)
- [二、`load_dotenv` 的本质原理(先理解这个)](#二、
用 dotenv 管理环境变量
一句话结论 :
dotenv 不是"高级技巧",而是现代工程的起点。
如果你在代码里见过下面这种写法,却不知道它到底解决了什么问题:
python
from dotenv import load_dotenv
import os
load_dotenv()
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
那这篇文章就是为你写的。
一、为什么要用环境变量?
1. 初学者最常见的错误
python
OPENAI_API_KEY = "sk-xxxxxxx"
问题有三:
- ❌ 密钥泄露风险
- ❌ 无法区分开发 / 测试 / 生产环境
- ❌ 代码不可复用
一旦 push 到 GitHub,你的 key 就"裸奔"了
2. 正确的工程思想
代码不应该包含任何"环境相关"的信息
包括:
- 密钥(API Key)
- 数据库密码
- 域名
- 开关配置
这些都应该交给 环境变量。
二、dotenv 是什么?
1. 定义
dotenv = 用文件的方式,管理环境变量
它解决的问题是:
本地开发时,没有方便的方式设置环境变量
2. .env 文件长什么样?
env
OPENAI_API_KEY=sk-xxxx
DB_HOST=localhost
DB_PORT=5432
DEBUG=true
特点:
- 不是 Python 代码
- 不需要引号
- 一行一个变量
三、安装与基本用法
1. 安装
bash
pip install python-dotenv
2. 最基础用法
python
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 本质做了什么?
text
.env 文件
↓
load_dotenv()
↓
os.environ["OPENAI_API_KEY"]
👉 dotenv 只是"帮你设置环境变量",不是配置中心
2. dotenv 加载优先级
默认行为:
- 系统环境变量
.env文件(不会覆盖已有环境变量)
python
load_dotenv(override=True) # 强制覆盖
五、dotenv + Python 项目标准结构
推荐目录结构
text
project/
├── app/
│ ├── main.py
│ └── config.py
├── .env
├── .gitignore
└── requirements.txt
config.py(推荐写法)
python
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()
使用:
python
from app.config import settings
print(settings.OPENAI_API_KEY)
六、dotenv + FastAPI / AI 项目实战
1. AI 项目(OpenAI / LangChain)
env
OPENAI_API_KEY=sk-xxx
OPENAI_BASE_URL=https://api.openai.com/v1
python
load_dotenv()
client = OpenAI(
api_key=os.getenv("OPENAI_API_KEY"),
base_url=os.getenv("OPENAI_BASE_URL"),
)
2. FastAPI 中只加载一次(重要)
❌ 错误写法:
python
# 每个文件都 load_dotenv
load_dotenv()
✅ 正确写法:
python
# main.py
load_dotenv()
其他文件只用 os.getenv
七、dotenv + .gitignore(安全必做)
.gitignore 必须包含
gitignore
.env
否则等于没用 dotenv。
八、常见坑(90% 初学者都会踩)
1️⃣ .env 不生效
检查顺序:
- 文件名是不是
.env - 是否在项目根目录
- 是否调用了
load_dotenv()
2️⃣ 用了 os.environ[] 报错
python
os.environ["OPENAI_API_KEY"] # KeyError
改用:
python
os.getenv("OPENAI_API_KEY") # None
3️⃣ .env 写了引号
env
OPENAI_API_KEY="sk-xxx"
❌ 不推荐
dotenv 会把引号当成内容的一部分
九、dotenv 和生产环境的关系
很重要的一点
dotenv 只用于本地开发
生产环境:
- Docker
- k8s
- 云平台
都应该用:
bash
export OPENAI_API_KEY=xxx
dotenv 的作用是:
模拟生产环境变量
这个问题问得非常到位 👍
因为 dotenv 到这里就"该退场了",而很多新手正是卡在「本地能跑,线上一团糟」。
我直接给你一套 生产环境"正确心智模型 + 可落地方案",你照着用就行。
load_dotenv 加载其他文件
load_dotenv()默认只加载当前目录或父目录里的.env,
但你可以显式指定任何文件路径、甚至加载多个文件。
二、load_dotenv 的本质原理(先理解这个)
load_dotenv() 实际做了什么?
-
找到一个
.env文件 -
逐行读取:
envKEY=value -
等价执行:
pythonos.environ["KEY"] = "value"
👉 它只是帮你往 os.environ 里塞值
三、最基础用法(你已经会的)
python
from dotenv import load_dotenv
load_dotenv()
等价于:
- 查找
.env - 加载成功就结束
- 找不到也不会报错
四、加载"别的环境变量文件"的正确方式 ⭐
✅ 方法 1:显式指定文件(最常用)
python
from dotenv import load_dotenv
load_dotenv(".env.dev")
或绝对路径:
python
load_dotenv("/Users/zhangyu/project/.env.prod")
✅ 方法 2:根据环境动态加载(推荐)
python
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
# .env.dev
OPENAI_API_KEY=dev-key
DEBUG=true
env
# .env.test
OPENAI_API_KEY=test-key
DEBUG=false
五、加载多个 env 文件(覆盖规则很重要)
python
load_dotenv(".env.base")
load_dotenv(".env.local", override=True)
覆盖规则
| 参数 | 说明 |
|---|---|
override=False(默认) |
已存在的不覆盖 |
override=True |
后面的覆盖前面的 |
👉 后加载的优先级更高
六、你很可能会踩的 3 个坑(提前告诉你)
❌ 坑 1:变量已经存在,load_dotenv 没效果
bash
export DEBUG=false
python
load_dotenv(".env.dev")
👉 默认不会覆盖
✅ 解决:
python
load_dotenv(".env.dev", override=True)
❌ 坑 2:文件路径不对
python
load_dotenv(".env.dev")
但你是从别的目录启动:
bash
uvicorn app.main:app
👉 当前工作目录不是项目根
✅ 推荐写法:
python
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(推荐)
python
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")