本文目录导读:

为Python脚本适配多环境配置(开发、测试、生产等),有几种成熟的方法,以下是常用的实现方案:
使用环境变量(推荐)
基础实现
import os
from dotenv import load_dotenv
# 加载.env文件
load_dotenv()
class Config:
# 数据库配置
DB_HOST = os.getenv('DB_HOST', 'localhost')
DB_PORT = int(os.getenv('DB_PORT', '5432'))
DB_NAME = os.getenv('DB_NAME', 'myapp')
DB_USER = os.getenv('DB_USER', 'admin')
DB_PASSWORD = os.getenv('DB_PASSWORD', '')
# 应用配置
DEBUG = os.getenv('DEBUG', 'False').lower() == 'true'
SECRET_KEY = os.getenv('SECRET_KEY', 'default-secret-key')
API_URL = os.getenv('API_URL', 'http://localhost:8000')
# 使用配置
config = Config()
print(f"当前环境: {os.getenv('ENV', 'development')}")
print(f"数据库: {config.DB_HOST}:{config.DB_PORT}")
多环境配置文件
# config.py
import os
from dotenv import load_dotenv
class BaseConfig:
"""基础配置"""
SECRET_KEY = os.getenv('SECRET_KEY', 'default-key')
API_TIMEOUT = 30
class DevelopmentConfig(BaseConfig):
"""开发环境"""
DEBUG = True
DB_URI = 'sqlite:///dev.db'
API_URL = 'http://localhost:8000'
class TestingConfig(BaseConfig):
"""测试环境"""
TESTING = True
DB_URI = 'sqlite:///test.db'
API_URL = 'http://test-api.example.com'
class ProductionConfig(BaseConfig):
"""生产环境"""
DEBUG = False
DB_URI = os.getenv('DATABASE_URL')
API_URL = 'https://api.example.com'
# 配置映射
config_map = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
def get_config():
env = os.getenv('FLASK_ENV', 'development')
config_class = config_map.get(env, config_map['default'])
return config_class()
# 使用
config = get_config()
print(f"当前环境: {os.getenv('FLASK_ENV', 'development')}")
print(f"数据库: {config.DB_URI}")
使用YAML配置文件
目录结构
project/
├── config/
│ ├── default.yaml
│ ├── development.yaml
│ ├── production.yaml
│ └── testing.yaml
└── app.py
配置加载
import yaml
import os
class ConfigLoader:
def __init__(self, env=None):
self.env = env or os.getenv('APP_ENV', 'development')
self.config = {}
self._load_config()
def _load_config(self):
config_dir = os.path.join(os.path.dirname(__file__), 'config')
# 加载默认配置
default_path = os.path.join(config_dir, 'default.yaml')
if os.path.exists(default_path):
with open(default_path, 'r') as f:
self.config.update(yaml.safe_load(f))
# 加载环境特定配置
env_path = os.path.join(config_dir, f'{self.env}.yaml')
if os.path.exists(env_path):
with open(env_path, 'r') as f:
self.config.update(yaml.safe_load(f))
# 环境变量覆盖(优先级最高)
self._override_with_env()
def _override_with_env(self):
"""环境变量覆盖配置"""
for key in self.config:
env_key = key.upper()
if env_key in os.environ:
self.config[key] = os.environ[env_key]
def __getattr__(self, name):
if name in self.config:
return self.config[name]
raise AttributeError(f"配置项 '{name}' 不存在")
# 使用示例
config = ConfigLoader()
print(f"数据库: {config.database}")
print(f"API密钥: {config.api_key}")
YAML配置文件示例
# config/default.yaml
database:
host: localhost
port: 5432
name: myapp
user: admin
redis:
host: localhost
port: 6379
api:
timeout: 30
retry: 3
# config/development.yaml
database:
name: myapp_dev
debug: true
# config/production.yaml
database:
host: ${DB_HOST}
name: myapp_prod
debug: false
使用配置类(更面向对象)
from dataclasses import dataclass, field
from typing import Optional
import os
from dotenv import load_dotenv
load_dotenv()
@dataclass
class DatabaseConfig:
host: str = 'localhost'
port: int = 5432
name: str = 'myapp'
user: str = 'admin'
password: str = ''
@classmethod
def from_env(cls):
return cls(
host=os.getenv('DB_HOST', 'localhost'),
port=int(os.getenv('DB_PORT', '5432')),
name=os.getenv('DB_NAME', 'myapp'),
user=os.getenv('DB_USER', 'admin'),
password=os.getenv('DB_PASSWORD', '')
)
@dataclass
class AppConfig:
debug: bool = False
secret_key: str = 'default'
api_url: str = 'http://localhost:8000'
database: DatabaseConfig = field(default_factory=DatabaseConfig)
@classmethod
def create(cls, env: Optional[str] = None):
env = env or os.getenv('APP_ENV', 'development')
if env == 'production':
return cls(
debug=False,
secret_key=os.getenv('SECRET_KEY', ''),
api_url='https://api.example.com',
database=DatabaseConfig.from_env()
)
elif env == 'testing':
return cls(
debug=True,
database=DatabaseConfig(
name='myapp_test',
host='localhost'
)
)
else: # development
return cls(
debug=True,
database=DatabaseConfig(
name='myapp_dev',
host='localhost'
)
)
# 使用
config = AppConfig.create()
print(f"调试模式: {config.debug}")
print(f"数据库: {config.database.host}:{config.database.port}/{config.database.name}")
使用配置管理库
安装 python-dotenv + configobj
pip install python-dotenv configobj
配置文件结构
# config/development.ini [app] debug = true secret_key = dev-secret-key [database] host = localhost port = 5432 name = myapp_dev [api] url = http://localhost:8000 timeout = 30
配置加载器
from configobj import ConfigObj
import os
from pathlib import Path
class Config:
_instance = None
_config = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._load()
return cls._instance
def _load(self):
env = os.getenv('APP_ENV', 'development')
config_path = Path(__file__).parent / 'config' / f'{env}.ini'
if not config_path.exists():
raise FileNotFoundError(f"配置文件不存在: {config_path}")
self._config = ConfigObj(str(config_path))
# 环境变量覆盖
self._override_with_env()
def _override_with_env(self):
"""环境变量覆盖配置"""
for section, values in self._config.items():
for key in values:
env_key = f"{section.upper()}_{key.upper()}"
if env_key in os.environ:
self._config[section][key] = os.environ[env_key]
def get(self, section, key, default=None):
try:
return self._config[section][key]
except KeyError:
return default
def __getattr__(self, name):
if name in self._config:
return self._config[name]
raise AttributeError(f"配置项 '{name}' 不存在")
# 单例使用
config = Config()
print(f"数据库: {config['database']['host']}")
print(f"调试: {config.get('app', 'debug', 'false')}")
推荐的最佳实践
项目结构
project/
├── config/
│ ├── __init__.py
│ ├── base.py
│ ├── development.py
│ ├── production.py
│ └── testing.py
├── .env
├── .env.development
├── .env.production
├── app.py
└── requirements.txt
完整的配置管理
# config/__init__.py
import os
from dotenv import load_dotenv
def load_config():
"""加载当前环境的配置"""
env = os.getenv('APP_ENV', 'development')
# 加载环境特定的.env文件
env_file = f'.env.{env}'
if os.path.exists(env_file):
load_dotenv(env_file)
# 加载基础.env文件
if os.path.exists('.env'):
load_dotenv('.env')
# 动态导入对应环境的配置模块
if env == 'production':
from .production import ProductionConfig
return ProductionConfig()
elif env == 'testing':
from .testing import TestingConfig
return TestingConfig()
else:
from .development import DevelopmentConfig
return DevelopmentConfig()
# config/base.py
class BaseConfig:
SECRET_KEY = os.getenv('SECRET_KEY')
SQLALCHEMY_TRACK_MODIFICATIONS = False
# config/development.py
from .base import BaseConfig
class DevelopmentConfig(BaseConfig):
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.getenv('DEV_DATABASE_URL',
'sqlite:///dev.db')
# app.py
from config import load_config
config = load_config()
app.config.from_object(config)
.env文件示例
# .env.development (开发环境) APP_ENV=development SECRET_KEY=dev-secret-key-123 DATABASE_URL=postgresql://localhost:5432/myapp_dev DEBUG=True API_URL=http://localhost:8000 # .env.production (生产环境) APP_ENV=production SECRET_KEY=prod-secret-key-456 DATABASE_URL=postgresql://user:pass@prod-db:5432/myapp DEBUG=False API_URL=https://api.example.com
启动脚本示例
# 开发环境 export APP_ENV=development && python app.py # 生产环境 export APP_ENV=production && python app.py # 或使用.env文件 APP_ENV=development python app.py
| 方法 | 优点 | 适用场景 |
|---|---|---|
| 环境变量 | 简单、安全、12-Factor | 云部署、容器化 |
| YAML/INI文件 | 结构化、易维护 | 复杂配置、团队协作 |
| 配置类 | 类型安全、IDE支持 | 中型项目 |
| 配置管理库 | 功能完整、可靠性高 | 大型项目 |
推荐方案:结合使用 环境变量 + YAML配置文件,既保证了灵活性,又便于团队协作,对于简单项目,单独使用环境变量即可。