Python代码质量:从规范到自动化检查
2026/5/7 2:10:55 网站建设 项目流程

Python代码质量:从规范到自动化检查

1. 技术分析

1.1 代码质量维度

维度描述工具
代码风格PEP 8规范black, isort
类型检查类型注解检查mypy
代码规范最佳实践flake8, pylint
安全检查潜在漏洞bandit, safety
测试覆盖代码测试比例coverage

1.2 工具对比

工具功能性能学习曲线
black代码格式化
flake8代码检查
mypy类型检查
pylint全面检查
ruff快速linting极快

2. 核心功能实现

2.1 代码格式化配置

# pyproject.toml [tool.black] line-length = 88 target-version = ['py39', 'py310', 'py311'] include = '\.pyi?$' exclude = ''' /( \.git | \.venv | build | dist )/ ''' [tool.isort] profile = "black" line_length = 88 known_first_party = ["src"] skip = [".venv", "build", "dist"] [tool.mypy] python_version = "3.9" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = false ignore_missing_imports = true [tool.ruff] line-length = 88 target-version = "py39" [tool.ruff.lint] select = ["E", "F", "W", "I", "N", "UP", "B", "C4"] ignore = ["E501"] # 行长度由black处理 [tool.coverage.run] source = ["src"] omit = ["*/tests/*", "*/test_*.py"] [tool.coverage.report] exclude_lines = [ "pragma: no cover", "if __name__ == .__main__.:", "raise AssertionError()", ]

2.2 单元测试实践

import pytest from typing import List, Optional class DataValidator: """数据验证器""" @staticmethod def validate_email(email: str) -> bool: """验证邮箱格式""" import re pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' return bool(re.match(pattern, email)) @staticmethod def validate_positive(value: float) -> bool: """验证正数""" return value > 0 @staticmethod def validate_in_range(value: float, min_val: float, max_val: float) -> bool: """验证范围""" return min_val <= value <= max_val class TestDataValidator: """数据验证器测试""" @pytest.mark.parametrize("email,expected", [ ("test@example.com", True), ("user.name@domain.co.uk", True), ("invalid-email", False), ("@domain.com", False), ("user@", False), ("", False), ]) def test_validate_email(self, email, expected): assert DataValidator.validate_email(email) == expected @pytest.mark.parametrize("value,expected", [ (1.0, True), (0.0, False), (-1.0, False), (100.5, True), ]) def test_validate_positive(self, value, expected): assert DataValidator.validate_positive(value) == expected def test_validate_in_range(self): assert DataValidator.validate_in_range(5, 0, 10) == True assert DataValidator.validate_in_range(0, 0, 10) == True assert DataValidator.validate_in_range(10, 0, 10) == True assert DataValidator.validate_in_range(-1, 0, 10) == False assert DataValidator.validate_in_range(11, 0, 10) == False class TestEdgeCases: """边界情况测试""" def test_empty_string(self): assert DataValidator.validate_email("") == False def test_unicode_email(self): assert DataValidator.validate_email("用户@例子.广告") == False def test_very_long_email(self): long_email = "a" * 100 + "@example.com" # 应该能处理但可能返回False(取决于具体实现) result = DataValidator.validate_email(long_email) assert isinstance(result, bool)

2.3 Mock与测试隔离

from unittest.mock import Mock, patch, MagicMock import pytest class APIClient: """API客户端""" def __init__(self, base_url: str): self.base_url = base_url self.session = None def fetch(self, endpoint: str) -> dict: """获取数据""" import requests response = requests.get(f"{self.base_url}/{endpoint}") return response.json() class TestAPIClient: """API客户端测试""" @patch('requests.get') def test_fetch_success(self, mock_get): """测试成功获取""" mock_response = Mock() mock_response.json.return_value = {"status": "success", "data": [1, 2, 3]} mock_get.return_value = mock_response client = APIClient("https://api.example.com") result = client.fetch("users") assert result["status"] == "success" assert result["data"] == [1, 2, 3] mock_get.assert_called_once_with("https://api.example.com/users") @patch('requests.get') def test_fetch_error(self, mock_get): """测试获取失败""" mock_get.side_effect = ConnectionError("Network error") client = APIClient("https://api.example.com") with pytest.raises(ConnectionError): client.fetch("users") def test_with_fixture(self, mock_get): """使用fixture的测试""" # fixture在conftest.py中定义 result = self.client.fetch("users") assert "status" in result

2.4 性能测试

import pytest import time class TestPerformance: """性能测试""" def test_sort_performance(self): """测试排序性能""" import random # 生成大量数据 data = [random.randint(0, 10000) for _ in range(10000)] start = time.perf_counter() sorted_data = sorted(data) elapsed = time.perf_counter() - start # 应该在1秒内完成 assert elapsed < 1.0, f"排序耗时 {elapsed:.2f}s,超过1秒" # 验证排序正确性 assert sorted_data == sorted(data) @pytest.mark.benchmark def test_list_comprehension_performance(self, benchmark): """基准测试列表推导式""" result = benchmark(lambda: [i**2 for i in range(10000)]) assert len(result) == 10000 # conftest.py def pytest_configure(config): config.addinivalue_line("markers", "benchmark: mark test as a benchmark") @pytest.fixture def sample_data(): """示例数据fixture""" return [i for i in range(100)]

3. 持续集成配置

3.1 pre-commit配置

# .pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: check-yaml - id: check-added-large-files - id: check-merge-conflict - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black language_version: python3.10 - repo: https://github.com/pycqa/isort rev: 5.12.0 hooks: - id: isort args: ["--profile", "black"] - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.0.261 hooks: - id: ruff args: ["--fix"] - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.3.0 hooks: - id: mypy additional_dependencies: [types-all]

3.2 GitHub Actions CI

# .github/workflows/ci.yml name: CI on: push: branches: [main, develop] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest strategy: matrix: python-version: ['3.9', '3.10', '3.11'] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e ".[dev]" - name: Lint with ruff run: ruff check src/ - name: Format check with black run: black --check src/ - name: Type check with mypy run: mypy src/ - name: Test with pytest run: | coverage run -m pytest tests/ coverage report --fail-under=80 - name: Upload coverage uses: codecov/codecov-action@v3 with: files: ./coverage.xml

4. 代码质量指标

4.1 覆盖率报告

# 运行测试并生成覆盖率报告 $ coverage run -m pytest tests/ $ coverage report -m Name Stmts Miss Cover Missing ----------------------------------------------------- src/validators.py 45 5 89% 23,45,67 src/models.py 78 12 85% 34,56,78 tests/test_validators.py 60 0 100% - ----------------------------------------------------- TOTAL 183 17 91%

4.2 复杂度分析

# 使用radon进行复杂度分析 from radon.metrics import mi_visit, h_visit from radon.complexity import cc_visit def analyze_complexity(filepath: str): """代码复杂度分析""" with open(filepath, 'r') as f: source = f.read() # 圈复杂度 complexity = cc_visit(source) print("圈复杂度:") for item in complexity: if item.classname: name = f"{item.classname}.{item.name}" else: name = item.name print(f" {name}: {item.complexity}") # 维护性指数 mi = mi_visit(source, multi=True) print(f"\n维护性指数: {mi:.1f}") # Halstead指标 from radon.metrics import h_visit halstead = h_visit(source) print(f"难度: {halstead.difficulty:.1f}")

5. 最佳实践

5.1 代码审查清单

- [ ] 代码符合PEP 8规范 - [ ] 函数和类有docstring - [ ] 类型注解完整 - [ ] 单元测试覆盖关键逻辑 - [ ] 没有硬编码的魔法数字 - [ ] 错误处理适当 - [ ] 没有安全漏洞 - [ ] 性能符合要求

5.2 提交前检查

#!/bin/bash # pre-commit-check.sh set -e echo "运行代码检查..." # 格式化 black --check src/ echo "✓ 格式化检查通过" # 检查import isort --check-only --diff src/ echo "✓ import检查通过" # Lint ruff check src/ echo "✓ Lint检查通过" # 类型检查 mypy src/ echo "✓ 类型检查通过" # 测试 pytest tests/ -v echo "✓ 测试通过" echo "所有检查通过!"

6. 总结

代码质量保障要点:

  1. 自动化:使用pre-commit和CI/CD自动化检查
  2. 覆盖率:保持80%+的测试覆盖率
  3. 持续改进:定期审视和改进代码质量

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询