Python脚本如何适配多环境配置

wen python案例 28

本文目录导读:

Python脚本如何适配多环境配置

  1. 使用环境变量(推荐)
  2. 使用YAML配置文件
  3. 使用配置类(更面向对象)
  4. 使用配置管理库
  5. 推荐的最佳实践
  6. 启动脚本示例

为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配置文件,既保证了灵活性,又便于团队协作,对于简单项目,单独使用环境变量即可。

抱歉,评论功能暂时关闭!