文章目录

Python 环境变量:os.environ 与 dotenv

发布于 2026-04-03 08:28:26 · 浏览 7 次 · 评论 0 条

Python 环境变量:os.environ 与 dotenv

Python 应用经常需要访问敏感信息(如 API 密钥、数据库密码)或配置参数(如调试开关、服务地址)。直接把这些值写死在代码里既不安全也不灵活。正确做法是使用环境变量——操作系统提供的键值对存储机制,程序运行时从中读取配置。

Python 标准库的 os.environ 对象能直接读取系统环境变量,而第三方库 python-dotenv 则允许你从 .env 文件加载变量,特别适合本地开发。两者结合使用,既能保证生产环境的安全性,又能简化开发流程。


第一步:理解 os.environ —— 读取系统环境变量

os.environ 是一个类似字典的对象,包含当前进程继承的所有环境变量。

创建一个测试脚本 read_env.py

import os

# 读取名为 'DATABASE_URL' 的环境变量
db_url = os.environ.get('DATABASE_URL')
print(f"数据库地址: {db_url}")

# 如果变量不存在,get() 返回 None;也可指定默认值
debug_mode = os.environ.get('DEBUG', 'False')
print(f"调试模式: {debug_mode}")

在终端中临时设置环境变量并运行脚本(Linux/macOS):

DATABASE_URL=postgresql://user:pass@localhost/db DEBUG=True python read_env.py

在 Windows PowerShell 中设置并运行

$env:DATABASE_URL="postgresql://user:pass@localhost/db"; $env:DEBUG="True"; python read_env.py

注意:通过命令行设置的变量仅在当前会话有效,关闭终端后消失。生产环境中,这些变量通常由部署平台(如 Docker、Heroku、Kubernetes)注入。


第二步:引入 dotenv —— 从文件加载环境变量

开发时频繁在命令行输入变量很麻烦。python-dotenv 库让你把变量写进 .env 文件,自动加载到 os.environ

安装 python-dotenv

pip install python-dotenv

在项目根目录创建 .env 文件

# .env
DATABASE_URL=postgresql://dev_user:dev_pass@localhost/myapp_dev
DEBUG=True
SECRET_KEY=my_super_secret_dev_key_123!
API_TIMEOUT=30

注意:.env 文件绝对不能提交到 Git立即在 .gitignore 中添加一行

.env

修改 read_env.py 以支持自动加载 .env

import os
from dotenv import load_dotenv

# 自动从 .env 文件加载变量到 os.environ
load_dotenv()

db_url = os.environ.get('DATABASE_URL')
debug_mode = os.environ.get('DEBUG', 'False')
secret_key = os.environ.get('SECRET_KEY')
api_timeout = int(os.environ.get('API_TIMEOUT', '10'))  # 转为整数

print(f"数据库地址: {db_url}")
print(f"调试模式: {debug_mode}")
print(f"密钥: {secret_key}")
print(f"API 超时(秒): {api_timeout}")

直接运行脚本(无需手动设置环境变量):

python read_env.py

load_dotenv() 默认查找当前工作目录下的 .env 文件。如果文件在其他位置,可传入路径:

load_dotenv('/path/to/your/.env')

第三步:处理变量类型与缺失情况

环境变量始终是字符串。需要布尔值、数字等类型时,必须手动转换,并处理无效值。

编写安全的类型转换函数

import os
from dotenv import load_dotenv

load_dotenv()

def get_bool_env(var_name, default=False):
    """将环境变量转为布尔值"""
    val = os.environ.get(var_name)
    if val is None:
        return default
    return val.lower() in ('true', '1', 'yes', 'on')

def get_int_env(var_name, default=0):
    """将环境变量转为整数"""
    val = os.environ.get(var_name)
    if val is None:
        return default
    try:
        return int(val)
    except ValueError:
        raise ValueError(f"环境变量 {var_name} 必须是整数,但得到: '{val}'")

# 使用示例
DEBUG = get_bool_env('DEBUG', False)
PORT = get_int_env('PORT', 8000)
MAX_RETRIES = get_int_env('MAX_RETRIES')  # 若未设置,默认为 0

永远不要直接用 os.environ['KEY'] 访问——如果变量不存在会抛出 KeyError始终使用 os.environ.get('KEY', default)


第四步:区分开发、测试与生产环境

不同环境需要不同配置。可通过一个主 .env 文件配合环境标识实现。

创建多个 .env 文件

  • .env(通用默认值)
  • .env.development(开发专用)
  • .env.testing(测试专用)
  • .env.production(生产专用,通常不放在代码库)

在代码中根据 ENV 变量选择加载哪个文件

import os
from dotenv import load_dotenv

# 先加载通用配置
load_dotenv('.env')

# 再根据 ENV 加载特定环境配置(会覆盖同名变量)
env = os.environ.get('ENV', 'development')
env_file = f".env.{env}"
if os.path.exists(env_file):
    load_dotenv(env_file, override=True)  # override=True 允许覆盖已有变量

# 现在所有变量都已就绪
DATABASE_URL = os.environ['DATABASE_URL']
DEBUG = get_bool_env('DEBUG')

运行时指定环境

ENV=production python app.py

生产环境中,通常不使用 .env 文件,而是由部署系统直接设置环境变量。此时 load_dotenv() 不会找到文件,但 os.environ 已包含所需值,逻辑依然成立。


第五步:安全最佳实践

环境变量虽比硬编码安全,但仍需注意:

  1. 绝不提交 .env 到版本控制:确保 .gitignore 包含 .env*
  2. 避免在日志中打印敏感变量:如 SECRET_KEY、密码等。
  3. 生产环境使用最小权限原则:数据库账号只给必要权限。
  4. 定期轮换密钥:尤其当怀疑泄露时。

下表总结了常见场景下的推荐做法:

场景 推荐方式 示例命令/文件
本地开发 .env 文件 + load_dotenv() echo "DEBUG=True" > .env
CI/CD 测试 在 CI 配置中设置环境变量 GitHub Actions 的 env: 字段
Docker 容器 docker run -e KEY=VAL--env-file docker run --env-file .env.prod
云平台 (如 Heroku) 在平台仪表盘设置 Config Vars Heroku Settings → Reveal Config Vars

第六步:高级技巧与陷阱规避

1. 变量引用与嵌套

.env 文件支持变量引用(需 python-dotenv>=0.19.0):

# .env
BASE_DIR=/app
LOG_PATH=${BASE_DIR}/logs
DB_USER=admin
DB_PASSWORD=secret123
DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@localhost/mydb
```

`load_dotenv()` 会自动解析 `${VAR}` 语法。

### 2. 处理多行值

`.env` 文件本身不支持真正的多行值。若需存储如私钥,可用以下变通:

```env
# 将换行符替换为 \n 字符串
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIE...A==\n-----END RSA PRIVATE KEY-----"

然后在代码中还原:

private_key = os.environ.get('PRIVATE_KEY', '').replace('\\n', '\n')

3. 验证必填变量

启动时检查关键变量是否存在:

required_vars = ['DATABASE_URL', 'SECRET_KEY']
missing = [var for var in required_vars if not os.environ.get(var)]
if missing:
    raise EnvironmentError(f"缺少必需的环境变量: {', '.join(missing)}")

4. 避免污染全局环境

load_dotenv() 默认修改全局 os.environ。如需隔离,可加载到独立字典:

from dotenv import dotenv_values

config = dotenv_values(".env")  # 返回 dict,不修改 os.environ
db_url = config['DATABASE_URL']

这种方式适合配置解析器,不影响其他模块。


最终整合:一个健壮的配置加载器

将上述实践封装成可复用的模块 config.py

# config.py
import os
from typing import Optional
from dotenv import load_dotenv

# 加载 .env 文件
load_dotenv()

class Config:
    def __init__(self):
        self._validate_required()

    def _validate_required(self):
        required = ['DATABASE_URL', 'SECRET_KEY']
        missing = [var for var in required if not os.environ.get(var)]
        if missing:
            raise EnvironmentError(f"缺少必需的环境变量: {', '.join(missing)}")

    @property
    def database_url(self) -> str:
        return os.environ['DATABASE_URL']

    @property
    def debug(self) -> bool:
        return os.environ.get('DEBUG', 'False').lower() in ('true', '1', 'yes')

    @property
    def secret_key(self) -> str:
        return os.environ['SECRET_KEY']

    @property
    def api_timeout(self) -> int:
        return int(os.environ.get('API_TIMEOUT', '30'))

# 全局实例
config = Config()

在应用其他部分直接使用:

from config import config

if config.debug:
    print("调试模式已启用")
connect_to_db(config.database_url)

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文