Python脚本代码复杂度Radon如何分析

wen 实用脚本 2

本文目录导读:

Python脚本代码复杂度Radon如何分析

  1. 安装Radon
  2. Radon支持的复杂度指标
  3. 命令行使用
  4. Python API使用
  5. 复杂度等级说明
  6. 生成HTML报告
  7. 与其他工具集成
  8. 实际案例分析
  9. 改进高复杂度代码
  10. 最佳实践

我来详细介绍使用Radon分析Python代码复杂度的方法:

安装Radon

pip install radon

Radon支持的复杂度指标

1 圈复杂度(Cyclomatic Complexity)

衡量代码的分支复杂度

# 示例代码:complex_functions.py
def simple_function(a, b):
    return a + b
def complex_function(x, y, z):
    if x > 0:
        if y > 0:
            if z > 0:
                return "all positive"
            else:
                return "z negative"
        else:
            return "y negative"
    else:
        return "x negative"

2 认知复杂度(Cognitive Complexity)

衡量代码的理解难度(Radon 4.0+)

命令行使用

1 分析单个文件

# 显示圈复杂度
radon cc complex_functions.py -s
# 显示评分(A-F等级)
radon cc complex_functions.py -a
# 显示详细输出
radon cc complex_functions.py -s -n

2 分析整个项目

# 递归分析所有Python文件
radon cc my_project/ -s
# 排除测试文件
radon cc my_project/ -e "test_*.py"

3 按复杂度排序

# 按复杂度降序排列
radon cc my_project/ -s --sort

Python API使用

import radon
from radon.complexity import cc_visit, cc_rank
from radon.raw import analyze
# 读取代码
with open('complex_functions.py', 'r') as f:
    code = f.read()
# 分析圈复杂度
results = cc_visit(code)
for func in results:
    print(f"Function: {func.name}")
    print(f"Complexity: {func.complexity}")
    print(f"Rank: {cc_rank(func.complexity)}")
    print(f"Line: {func.lineno}")
    print("---")

复杂度等级说明

def get_complexity_rank(complexity):
    """圈复杂度等级"""
    ranks = {
        'A': (1, 5, '低风险'),
        'B': (6, 10, '较低风险'),
        'C': (11, 20, '中等风险'),
        'D': (21, 30, '较高风险'),
        'E': (31, 40, '高风险'),
        'F': (41, float('inf'), '极高的风险')
    }
    for rank, (min_val, max_val, desc) in ranks.items():
        if min_val <= complexity <= max_val:
            return f"{rank}级: {desc}"
    return "未知等级"
# 示例
print(get_complexity_rank(3))   # A级
print(get_complexity_rank(15))  # C级
print(get_complexity_rank(50))  # F级

生成HTML报告

import json
from radon.complexity import cc_visit
from radon.raw import analyze
def generate_complexity_report(code, filename):
    """生成可读性报告"""
    # 圈复杂度
    cc_results = cc_visit(code)
    # 原始指标
    raw_metrics = analyze(code)
    report = {
        'file': filename,
        'complexity': {
            'functions': [],
            'average': 0
        },
        'raw_metrics': {
            'lines': raw_metrics.loc,
            'code_lines': raw_metrics.sloc,
            'comments': raw_metrics.comments,
            'docstrings': raw_metrics.multi,
        }
    }
    complexities = []
    for func in cc_results:
        func_info = {
            'name': func.name,
            'type': func.type,
            'complexity': func.complexity,
            'rank': cc_rank(func.complexity),
            'line': func.lineno
        }
        report['complexity']['functions'].append(func_info)
        complexities.append(func.complexity)
    if complexities:
        report['complexity']['average'] = sum(complexities) / len(complexities)
    return report
# 使用示例
code = """
def func1(x):
    if x > 0:
        return x
    return -x
def func2(a, b, c):
    if a > b:
        if b > c:
            return a
        else:
            return c
    else:
        return b
"""
report = generate_complexity_report(code, 'example.py')
print(json.dumps(report, indent=2))

与其他工具集成

1 集成到测试框架

import pytest
from radon.complexity import cc_visit
def test_complexity():
    """确保代码复杂度不超过阈值"""
    MAX_COMPLEXITY = 10
    with open('my_module.py', 'r') as f:
        code = f.read()
    results = cc_visit(code)
    for func in results:
        assert func.complexity <= MAX_COMPLEXITY, \
            f"{func.name} 复杂度 {func.complexity} 超过阈值 {MAX_COMPLEXITY}"

2 集成到CI/CD

# .github/workflows/complexity-check.yml
name: Code Complexity Check
on: [push, pull_request]
jobs:
  complexity:
    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: |
          pip install radon
      - name: Check complexity
        run: |
          radon cc . -a --exclude "tests/*,.venv/*"

实际案例分析

# bad_complexity.py - 高复杂度示例
def process_data(data, options):
    """处理数据的高复杂度函数"""
    if options.get('validate'):
        if not data:
            return {'error': 'No data'}
        if options.get('type') == 'csv':
            if isinstance(data, str):
                lines = data.split('\n')
                rows = [line.split(',') for line in lines]
                if options.get('header'):
                    if rows:
                        headers = rows[0]
                        data = rows[1:]
                    else:
                        return {'error': 'Empty CSV'}
                else:
                    headers = None
                    data = rows
            else:
                return {'error': 'Invalid CSV format'}
        elif options.get('type') == 'json':
            import json
            try:
                if isinstance(data, str):
                    data = json.loads(data)
                if isinstance(data, dict):
                    if options.get('flatten'):
                        flattened = {}
                        for key, value in data.items():
                            if isinstance(value, dict):
                                for sub_key, sub_value in value.items():
                                    flattened[f"{key}_{sub_key}"] = sub_value
                            else:
                                flattened[key] = value
                        data = flattened
            except json.JSONDecodeError:
                return {'error': 'Invalid JSON'}
        else:
            return {'error': f'Unsupported type: {options.get("type")}'}
    # ... 更多条件和循环
    return {'result': data}
# 复杂度分析结果
# radon cc bad_complexity.py -s
# 会显示函数复杂度较高

改进高复杂度代码

# good_complexity.py - 重构后的低复杂度代码
class DataProcessor:
    """处理数据类"""
    def process(self, data, options):
        """主处理方法"""
        if options.get('validate'):
            validation_result = self._validate_data(data, options)
            if validation_result['error']:
                return validation_result
        processed_data = self._process_by_type(data, options)
        return {'result': processed_data}
    def _validate_data(self, data, options):
        """验证数据"""
        if not data:
            return {'error': 'No data'}
        return {'error': None}
    def _process_by_type(self, data, options):
        """根据类型处理数据"""
        processors = {
            'csv': self._process_csv,
            'json': self._process_json,
        }
        processor = processors.get(options.get('type'))
        if not processor:
            return {'error': f'Unsupported type: {options.get("type")}'}
        return processor(data, options)
    def _process_csv(self, data, options):
        """处理CSV数据"""
        if not isinstance(data, str):
            return {'error': 'Invalid CSV format'}
        rows = [line.split(',') for line in data.split('\n')]
        return self._extract_data(rows, options.get('header'))
    def _process_json(self, data, options):
        """处理JSON数据"""
        import json
        parsed_data = self._parse_json(data)
        if parsed_data is None:
            return {'error': 'Invalid JSON'}
        if options.get('flatten'):
            parsed_data = self._flatten_dict(parsed_data)
        return parsed_data
    # 更多独立的小函数...

最佳实践

1 设置复杂度阈值

# 项目配置
COMPLEXITY_THRESHOLDS = {
    'function': 10,  # 函数复杂度阈值
    'method': 10,    # 方法复杂度阈值
    'module': 30,    # 模块复杂度阈值
}

2 自动化检查

# complexity_checker.py
import os
import sys
from radon.complexity import cc_visit
def check_project_complexity(project_path, max_complexity=10):
    """检查项目复杂度"""
    issues = []
    for root, dirs, files in os.walk(project_path):
        # 跳过隐藏目录和虚拟环境
        dirs[:] = [d for d in dirs if not d.startswith('.') and d != 'venv']
        for file in files:
            if file.endswith('.py'):
                filepath = os.path.join(root, file)
                with open(filepath, 'r', encoding='utf-8') as f:
                    try:
                        results = cc_visit(f.read())
                        for func in results:
                            if func.complexity > max_complexity:
                                issues.append({
                                    'file': filepath,
                                    'function': func.name,
                                    'complexity': func.complexity,
                                    'line': func.lineno
                                })
                    except SyntaxError:
                        print(f"语法错误: {filepath}")
    return issues
# 使用
issues = check_project_complexity('./my_project')
if issues:
    print("发现高复杂度代码:")
    for issue in issues:
        print(f"  {issue['file']}:{issue['line']} - "
              f"{issue['function']}({issue['complexity']})")
else:
    print("所有代码复杂度在阈值范围内")

通过以上方法,你可以全面分析Python代码的复杂度,并持续改进代码质量。

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