环境隔离与依赖管理

在实际项目中,我们经常遇到不同项目需要不同Python版本或依赖包的情况。曾经遇到过一个典型问题:一个使用TensorFlow 1.x的旧项目和一个需要TensorFlow 2.x的新项目同时在开发,如果不做好环境隔离,就会导致依赖冲突。

推荐方案

  • 使用pyenv管理多版本Python
  • 使用venv创建虚拟环境
  • 使用requirements.txt或Pipfile精确记录依赖
# 创建虚拟环境的最佳实践
python -m venv project_env
source project_env/bin/activate  # Linux/Mac
# project_env\Scripts\activate  # Windows

# 生成精确的依赖文件
pip freeze > requirements.txt

# 安装时指定版本
pip install -r requirements.txt

代码结构与模块化设计

良好的项目结构是维护性的基础。曾经接手过一个几千行代码都放在单个文件中的项目,每次修改都如履薄冰。

推荐的项目结构

my_project/
├── src/
│   ├── __init__.py
│   ├── core/
│   │   ├── __init__.py
│   │   ├── data_processor.py
│   │   └── model.py
│   └── utils/
│       ├── __init__.py
│       └── helpers.py
├── tests/
├── docs/
├── setup.py
└── requirements.txt

模块设计原则

  • 单一职责:每个模块只负责一个明确的功能
  • 低耦合高内聚:模块间依赖最小化,模块内部功能相关性强
  • 清晰的接口:通过__init__.py暴露主要功能

配置管理的艺术

硬编码配置是项目维护的噩梦。曾经因为数据库连接字符串硬编码在多个文件中,迁移数据库时差点崩溃。

配置管理方案

# config.py
import os
from dataclasses import dataclass

@dataclass
class Config:
    DEBUG: bool = os.getenv('DEBUG', 'False').lower() == 'true'
    DATABASE_URL: str = os.getenv('DATABASE_URL', 'sqlite:///default.db')
    MAX_WORKERS: int = int(os.getenv('MAX_WORKERS', '4'))

# 使用示例
from config import Config
cfg = Config()

环境区分

  • 开发环境:使用.env文件或环境变量
  • 测试环境:独立的配置文件
  • 生产环境:通过环境变量或配置服务

异常处理与日志记录

没有良好的异常处理和日志记录,线上问题排查就像大海捞针。曾经因为一个简单的网络超时没有正确处理,导致整个服务雪崩。

异常处理最佳实践

import logging
from typing import Optional

logger = logging.getLogger(__name__)

def process_data(data: list) -> Optional[dict]:
    try:
        # 业务逻辑
        result = complex_operation(data)
        return result
    except ValueError as e:
        logger.warning(f"数据格式错误: {e}")
        return None
    except ConnectionError as e:
        logger.error(f"网络连接失败: {e}")
        raise  # 重新抛出,让上层处理
    except Exception as e:
        logger.exception(f"未知错误: {e}")
        return None

日志配置

import logging
import sys

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('app.log'),
        logging.StreamHandler(sys.stdout)
    ]
)

测试与质量保证

没有测试的代码就像没有安全网的高空作业。曾经因为一个简单的边界条件没测试,导致生产环境数据损坏。

测试策略

# tests/test_core.py
import pytest
from src.core.data_processor import DataProcessor

class TestDataProcessor:
    def test_process_valid_data(self):
        processor = DataProcessor()
        data = [1, 2, 3]
        result = processor.process(data)
        assert result == [2, 4, 6]
    
    def test_process_empty_data(self):
        processor = DataProcessor()
        with pytest.raises(ValueError):
            processor.process([])
    
    @pytest.mark.parametrize("input_data,expected", [
        ([1], [2]),
        ([1, 2, 3], [2, 4, 6]),
    ])
    def test_process_multiple_cases(self, input_data, expected):
        processor = DataProcessor()
        assert processor.process(input_data) == expected

质量保证工具链

  • 静态检查:flake8, mypy
  • 代码格式化:black, isort
  • 安全扫描:bandit
  • 测试覆盖:pytest-cov

持续集成与部署

手动部署容易出错且效率低下。曾经因为忘记某个部署步骤,导致服务中断半小时。

GitHub Actions示例

name: Python CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.9'
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
        pip install pytest pytest-cov
    - name: Run tests
      run: |
        pytest --cov=src tests/

这些实践经验来自多年Python开发的血泪教训,希望能帮助大家避开我踩过的坑。