1. python-dotenv 是什么? #
python-dotenv 是 Python 中用于管理环境变量的工具,它允许你从 .env 文件加载环境变量到 os.environ 中,让配置管理更简单、更安全。
1.1 为什么需要 python-dotenv? #
在开发应用时,经常需要存储敏感信息(如数据库密码、API 密钥等)。如果直接写在代码中,会有以下问题:
- 安全问题:代码可能被提交到版本控制系统,泄露敏感信息
- 环境差异:开发、测试、生产环境需要不同的配置
- 管理困难:修改配置需要改代码,容易出错
使用 python-dotenv 的好处:
- 安全:敏感信息存储在
.env文件中,不提交到版本控制 - 灵活:不同环境可以使用不同的
.env文件 - 简单:一行代码就能加载所有配置
简单理解:
- 环境变量:存储在系统或文件中的配置信息
- .env 文件:存储环境变量的文本文件
- python-dotenv:从
.env文件读取环境变量的工具
1.2 基本概念 #
# 导入 python-dotenv
from dotenv import load_dotenv
import os
# 加载 .env 文件中的环境变量
load_dotenv()
# 现在可以使用环境变量
api_key = os.getenv('API_KEY')
print(f"API Key: {api_key}")说明:
load_dotenv()从.env文件加载环境变量os.getenv()获取环境变量的值- 环境变量会被加载到
os.environ中
2. 前置知识 #
在学习 python-dotenv 之前,你需要了解以下基础知识。
2.1 什么是环境变量? #
环境变量(Environment Variables) 是存储在操作系统中的键值对,用于配置应用程序。
简单理解:
- 环境变量就像是一个全局的配置字典
- 键(Key)是变量名,值(Value)是变量值
- 程序可以从环境变量中读取配置
在 Python 中使用环境变量:
# 导入 os 模块
import os
# 获取环境变量
# getenv() 获取环境变量的值,如果不存在返回 None
api_key = os.getenv('API_KEY')
# 获取环境变量(带默认值)
# 如果环境变量不存在,返回默认值
port = os.getenv('PORT', '8000')
# 直接访问 os.environ(字典格式)
# 如果环境变量不存在,会抛出 KeyError
database_url = os.environ['DATABASE_URL']2.2 什么是 .env 文件? #
.env 文件是一个文本文件,用于存储环境变量。通常放在项目根目录。
简单理解:
.env文件是一个配置文件- 每行一个环境变量,格式:
KEY=value - 以
#开头的是注释
示例 .env 文件:
# 这是注释
API_KEY=sk-1234567890abcdef
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
DEBUG=True
PORT=80002.3 Python 基础 #
你需要了解:
- 基本的 Python 语法
os模块的使用(os.getenv()、os.environ)- 文件操作(读取文件)
3. 安装 #
3.1 安装 python-dotenv #
使用 pip 安装:
# 使用 pip 安装
pip install python-dotenv说明:
python-dotenv是第三方库,需要单独安装- 使用
pip install命令安装
3.2 验证安装 #
安装后可以验证是否安装成功:
# 导入 dotenv 模块
import dotenv
# 打印版本号
# __version__ 是模块的版本属性
print(f"python-dotenv 版本:{dotenv.__version__}")
# 主程序入口
if __name__ == "__main__":
print("python-dotenv 安装成功!")说明:
dotenv.__version__显示安装的版本- 如果导入成功,说明安装正确
4. .env 文件格式 #
4.1 基本语法 #
.env 文件使用简单的键值对格式:
# .env 文件示例
# 这是注释,以 # 开头
# 基本格式:KEY=value
API_KEY=sk-1234567890abcdef
# 带空格的值需要用引号
APP_NAME="My Application"
# 数字值(仍然是字符串)
PORT=8000
# 布尔值(仍然是字符串)
DEBUG=True说明:
- 每行一个环境变量
- 格式:
KEY=value(等号两边可以有空格) - 以
#开头的是注释 - 值中的空格需要用引号包裹
4.2 实际项目示例 #
# .env 文件示例
# 数据库配置
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
# API 密钥
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxx
# 应用配置
DEBUG=True
SECRET_KEY=your-secret-key-here
ALLOWED_HOSTS=localhost,127.0.0.1,example.com
# 服务配置
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True说明:
- 不同类型的配置可以分组(用注释分隔)
- 敏感信息(如密码、密钥)存储在
.env文件中 - 不要将
.env文件提交到版本控制系统
5. 基本使用 #
5.1 最简单的用法 #
# 导入必要的模块
import os
from dotenv import load_dotenv
# 加载 .env 文件
# load_dotenv() 会自动查找当前目录下的 .env 文件
load_dotenv()
# 获取环境变量
# os.getenv() 从 os.environ 中获取环境变量的值
api_key = os.getenv('API_KEY')
database_url = os.getenv('DATABASE_URL')
# 打印环境变量
print(f"API Key: {api_key}")
print(f"Database URL: {database_url}")
# 主程序入口
if __name__ == "__main__":
print("环境变量加载成功!")说明:
load_dotenv()从.env文件加载环境变量到os.environos.getenv()获取环境变量的值- 如果环境变量不存在,
os.getenv()返回None
5.2 指定 .env 文件路径 #
可以指定 .env 文件的具体路径:
# 导入必要的模块
from dotenv import load_dotenv
from pathlib import Path
# 方法 1:指定相对路径
# 加载当前目录下的 .env.local 文件
load_dotenv('.env.local')
# 方法 2:指定绝对路径
# 加载指定路径的 .env 文件
load_dotenv('/absolute/path/to/.env')
# 方法 3:使用 Path 对象
# Path 对象可以更方便地处理路径
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)
# 主程序入口
if __name__ == "__main__":
print("从指定路径加载 .env 文件")说明:
- 可以指定
.env文件的具体路径 - 使用
Path对象可以更方便地处理路径 - 如果文件不存在,
load_dotenv()不会报错(静默失败)
5.3 配置选项 #
load_dotenv() 函数支持多个配置选项:
# 导入必要的模块
from dotenv import load_dotenv
# 加载 .env 文件,使用配置选项
load_dotenv(
dotenv_path='.env', # .env 文件路径(默认是 .env)
verbose=True, # 显示加载信息(默认是 False)
override=False, # 是否覆盖已存在的环境变量(默认是 False)
interpolate=True, # 是否进行变量插值(默认是 True)
encoding='utf-8' # 文件编码(默认是 utf-8)
)
# 主程序入口
if __name__ == "__main__":
print("使用配置选项加载 .env 文件")说明:
dotenv_path:指定.env文件路径verbose:是否显示加载信息override:是否覆盖已存在的环境变量interpolate:是否进行变量插值(如${VAR})encoding:文件编码
6. 常用功能 #
6.1 获取环境变量(带默认值) #
# 导入必要的模块
import os
from dotenv import load_dotenv
# 加载 .env 文件
load_dotenv()
# 获取环境变量(带默认值)
# os.getenv() 的第二个参数是默认值
# 如果环境变量不存在,返回默认值
port = os.getenv('PORT', '8000')
debug = os.getenv('DEBUG', 'False')
api_key = os.getenv('API_KEY', 'default-key')
# 打印结果
print(f"Port: {port}")
print(f"Debug: {debug}")
print(f"API Key: {api_key}")
# 主程序入口
if __name__ == "__main__":
print("获取环境变量(带默认值)")说明:
os.getenv(key, default)可以指定默认值- 如果环境变量不存在,返回默认值
- 这样可以避免
None值导致的错误
6.2 类型转换 #
环境变量都是字符串类型,需要时可以进行类型转换:
# 导入必要的模块
import os
from dotenv import load_dotenv
# 加载 .env 文件
load_dotenv()
# 字符串类型(默认)
# 环境变量都是字符串类型
debug_str = os.getenv('DEBUG', 'False')
print(f"Debug (字符串): {debug_str}, 类型: {type(debug_str).__name__}")
# 转换为布尔值
# 将字符串转换为布尔值
debug_bool = os.getenv('DEBUG', 'False').lower() == 'true'
print(f"Debug (布尔值): {debug_bool}, 类型: {type(debug_bool).__name__}")
# 转换为整数
# 使用 int() 将字符串转换为整数
port = int(os.getenv('PORT', '8000'))
print(f"Port: {port}, 类型: {type(port).__name__}")
# 转换为列表
# 使用 split() 将逗号分隔的字符串转换为列表
hosts_str = os.getenv('ALLOWED_HOSTS', 'localhost,127.0.0.1')
hosts = [item.strip() for item in hosts_str.split(',') if item.strip()]
print(f"Allowed Hosts: {hosts}, 类型: {type(hosts).__name__}")
# 主程序入口
if __name__ == "__main__":
print("类型转换示例")说明:
- 环境变量都是字符串类型
- 需要时可以使用
int()、bool()等函数转换 - 列表可以使用
split()方法分割
6.3 辅助函数 #
可以创建辅助函数来简化类型转换:
# 导入必要的模块
import os
from dotenv import load_dotenv
# 加载 .env 文件
load_dotenv()
# 定义辅助函数:获取布尔值环境变量
def get_env_bool(key, default=False):
"""获取布尔值环境变量"""
# 获取环境变量的值(字符串)
value = os.getenv(key, str(default))
# 转换为小写并检查是否为 'true'、'1' 或 'yes'
return value.lower() in ('true', '1', 'yes')
# 定义辅助函数:获取列表环境变量
def get_env_list(key, default=''):
"""获取列表环境变量"""
# 获取环境变量的值
value = os.getenv(key, default)
# 按逗号分割,去除空白,过滤空字符串
return [item.strip() for item in value.split(',') if item.strip()]
# 定义辅助函数:获取整数环境变量
def get_env_int(key, default=0):
"""获取整数环境变量"""
# 获取环境变量的值
value = os.getenv(key, str(default))
# 转换为整数
try:
return int(value)
except ValueError:
# 如果转换失败,返回默认值
return default
# 使用辅助函数
debug = get_env_bool('DEBUG', False)
port = get_env_int('PORT', 8000)
hosts = get_env_list('ALLOWED_HOSTS', 'localhost')
# 打印结果
print(f"Debug: {debug}, 类型: {type(debug).__name__}")
print(f"Port: {port}, 类型: {type(port).__name__}")
print(f"Hosts: {hosts}, 类型: {type(hosts).__name__}")
# 主程序入口
if __name__ == "__main__":
print("使用辅助函数获取环境变量")说明:
- 辅助函数可以简化类型转换
get_env_bool()将字符串转换为布尔值get_env_list()将逗号分隔的字符串转换为列表get_env_int()将字符串转换为整数
6.4 直接获取所有变量(不修改 os.environ) #
dotenv_values() 可以直接获取 .env 文件中的所有变量,而不修改 os.environ:
# 导入必要的模块
from dotenv import dotenv_values
# 直接获取 .env 文件中的所有变量
# dotenv_values() 返回一个字典,包含所有环境变量
# 不会修改 os.environ
config = dotenv_values('.env')
# 打印所有配置
print("所有环境变量:")
for key, value in config.items():
print(f" {key} = {value}")
# 访问特定变量
api_key = config.get('API_KEY')
database_url = config.get('DATABASE_URL')
print(f"\nAPI Key: {api_key}")
print(f"Database URL: {database_url}")
# 主程序入口
if __name__ == "__main__":
print("直接获取所有环境变量")说明:
dotenv_values()返回字典,包含所有环境变量- 不会修改
os.environ - 适合只需要读取配置,不需要修改系统环境变量的场景
7. 实际应用示例 #
7.1 简单的配置管理 #
创建一个简单的配置管理类:
# 导入必要的模块
import os
from dotenv import load_dotenv
# 加载 .env 文件
load_dotenv()
# 定义配置类
class Config:
"""应用配置类"""
# 从环境变量获取配置
# SECRET_KEY 是应用密钥(用于加密等)
SECRET_KEY = os.getenv('SECRET_KEY', 'default-secret-key')
# DEBUG 模式(开发时启用)
DEBUG = os.getenv('DEBUG', 'False').lower() == 'true'
# 数据库 URL
DATABASE_URL = os.getenv('DATABASE_URL', 'sqlite:///db.sqlite3')
# API 密钥
API_KEY = os.getenv('API_KEY')
# 端口号(转换为整数)
PORT = int(os.getenv('PORT', '8000'))
# 允许的主机(转换为列表)
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS', 'localhost').split(',')
# 创建配置实例
config = Config()
# 打印配置信息
print("应用配置:")
print(f" Secret Key: {config.SECRET_KEY[:20]}...")
print(f" Debug: {config.DEBUG}")
print(f" Database URL: {config.DATABASE_URL}")
print(f" API Key: {config.API_KEY}")
print(f" Port: {config.PORT}")
print(f" Allowed Hosts: {config.ALLOWED_HOSTS}")
# 主程序入口
if __name__ == "__main__":
print("配置管理示例")说明:
- 使用类来组织配置,更清晰
- 所有配置都从环境变量读取
- 可以设置默认值,避免配置缺失
7.2 配置验证 #
在应用启动时验证必要的配置是否存在:
# 导入必要的模块
import os
from dotenv import load_dotenv
# 加载 .env 文件
load_dotenv()
# 定义配置验证器
class ConfigValidator:
"""配置验证器:检查必要的配置是否存在"""
# 定义必要的配置项
REQUIRED_KEYS = [
'SECRET_KEY',
'DATABASE_URL',
'API_KEY'
]
@classmethod
def validate(cls):
"""验证配置"""
# 存储缺失的配置项
missing = []
# 检查每个必要的配置项
for key in cls.REQUIRED_KEYS:
# 如果配置项不存在或为空,添加到缺失列表
if not os.getenv(key):
missing.append(key)
# 如果有缺失的配置项,抛出异常
if missing:
raise ValueError(f"缺少必要的环境变量: {', '.join(missing)}")
# 所有配置都存在
print("✓ 所有必要配置已设置")
# 主程序入口
if __name__ == "__main__":
# 在应用启动时验证配置
try:
ConfigValidator.validate()
print("应用可以正常启动")
except ValueError as e:
print(f"配置错误:{e}")
print("请检查 .env 文件,确保所有必要的配置都已设置")说明:
- 配置验证器检查必要的配置是否存在
- 如果缺少配置,抛出异常,避免应用启动后出错
- 在应用启动时调用验证器
8. 最佳实践 #
8.1 项目结构 #
推荐的项目结构:
my_project/
├── .env.example # 示例配置(提交到版本控制)
├── .env # 实际配置(不提交到版本控制)
├── .gitignore # 包含 .env
├── config.py # 配置加载代码
└── main.py # 主程序说明:
.env.example是示例文件,包含配置项但不包含真实值.env是实际配置文件,包含真实值,不提交到版本控制.gitignore中应该包含.env
8.2 创建 .env.example #
创建 .env.example 文件作为模板:
# .env.example
# 复制此文件为 .env 并填写实际值
# 数据库配置
DATABASE_URL=postgresql://user:password@localhost:5432/dbname
# 安全密钥
SECRET_KEY=your-secret-key-here
# API 密钥
API_KEY=your-api-key-here
# 应用设置
DEBUG=True
PORT=8000
ALLOWED_HOSTS=localhost,127.0.0.1说明:
.env.example是模板文件,不包含敏感信息- 新开发者可以复制
.env.example为.env并填写实际值 .env.example可以提交到版本控制
8.3 .gitignore 配置 #
在 .gitignore 中添加 .env 文件:
# .gitignore
# 忽略 .env 文件(包含敏感信息)
.env
# 忽略所有 .env 相关文件
.env.local
.env.*.local说明:
.env文件包含敏感信息,不应该提交到版本控制- 在
.gitignore中添加.env,确保不会被提交
9. 常见问题 #
9.1 .env 文件未找到 #
如果 .env 文件不在当前目录,需要指定路径:
# 导入必要的模块
from dotenv import load_dotenv
from pathlib import Path
# 确保路径正确
# Path(__file__).parent 获取当前文件所在目录
env_path = Path(__file__).parent / '.env'
# 检查文件是否存在
if env_path.exists():
# 文件存在,加载环境变量
load_dotenv(env_path)
print(f"✓ 已加载 {env_path}")
else:
# 文件不存在,显示警告
print(f"警告: 未找到 {env_path}")
# 主程序入口
if __name__ == "__main__":
print("检查 .env 文件是否存在")说明:
- 使用
Path(__file__).parent获取当前文件所在目录 - 检查文件是否存在,避免静默失败
- 如果文件不存在,显示警告信息
9.2 环境变量未加载 #
如果环境变量未加载,可以检查:
# 导入必要的模块
import os
from dotenv import load_dotenv
# 使用 override=True 强制覆盖
# override=True 会覆盖已存在的环境变量
load_dotenv(override=True)
# 检查是否加载成功
# 尝试获取一个环境变量
my_key = os.environ.get('MY_KEY')
if my_key:
print(f"✓ 环境变量已加载: MY_KEY = {my_key}")
else:
print("✗ 环境变量未加载,请检查 .env 文件")
# 打印所有环境变量(仅用于调试)
# print("所有环境变量:")
# for key, value in os.environ.items():
# if key.startswith('MY_'):
# print(f" {key} = {value}")
# 主程序入口
if __name__ == "__main__":
print("检查环境变量是否加载")说明:
- 使用
override=True强制覆盖已存在的环境变量 - 检查环境变量是否存在
- 可以打印所有环境变量用于调试
9.3 环境变量值为 None #
如果环境变量不存在,os.getenv() 返回 None,需要处理:
# 导入必要的模块
import os
from dotenv import load_dotenv
# 加载 .env 文件
load_dotenv()
# 方法 1:使用默认值
# 如果环境变量不存在,使用默认值
api_key = os.getenv('API_KEY', 'default-key')
print(f"API Key: {api_key}")
# 方法 2:检查是否为 None
# 如果环境变量不存在,进行特殊处理
database_url = os.getenv('DATABASE_URL')
if database_url is None:
print("警告: DATABASE_URL 未设置,使用默认值")
database_url = 'sqlite:///db.sqlite3'
print(f"Database URL: {database_url}")
# 方法 3:使用 os.environ.get()(与方法 1 相同)
# os.environ.get() 也可以指定默认值
secret_key = os.environ.get('SECRET_KEY', 'default-secret-key')
print(f"Secret Key: {secret_key}")
# 主程序入口
if __name__ == "__main__":
print("处理环境变量为 None 的情况")说明:
- 使用默认值避免
None值 - 检查是否为
None并进行特殊处理 os.environ.get()也可以指定默认值
10. 总结 #
10.1 核心概念回顾 #
- python-dotenv:从
.env文件加载环境变量的工具 - .env 文件:存储环境变量的文本文件
- load_dotenv():加载
.env文件中的环境变量 - os.getenv():获取环境变量的值
10.2 基本使用流程 #
- 安装:
pip install python-dotenv - 创建
.env文件:在项目根目录创建.env文件 - 加载环境变量:
load_dotenv() - 使用环境变量:
os.getenv('KEY')
10.3 使用 python-dotenv 的好处 #
- 安全:敏感信息不写在代码中
- 灵活:不同环境可以使用不同的配置
- 简单:一行代码就能加载所有配置
- 版本控制友好:
.env文件不提交,保护敏感信息