mcpField
P
py-complexity

py-complexity

github.com/l-mb开发5667 次安装34 周前更新
代码仓库

关于 py-complexity

py-complexity 是面向 Python 开发者的代码复杂度测量与降低技能。它指导重构和趋势跟踪,并可在 CI/CD 中执行阈值检查。

主要功能

  1. 01测量圈复杂度与认知复杂度
  2. 02查找复杂函数和过长文件
  3. 03应用代码重构模式
  4. 04跟踪 Git 历史中的复杂度趋势
  5. 05在 CI/CD 中执行阈值检查

适用场景

  1. 01识别需要重构的 Python 代码
  2. 02减少嵌套与重复逻辑
  3. 03在开发过程中监控复杂度变化

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/l-mb/python-refactoring-skills --skill py-complexity

安装后在 Claude Code 中输入/py-complexity确认已加载。

详细介绍

Python 复杂度降低

降低代码复杂度,提升可维护性和可理解性。

有效利用上下文窗口。

目标

  1. 衡量圈复杂度和认知复杂度
  2. 找出过于复杂的函数和模块
  3. 找出过长的文件(代码文件不超过 500 行,除非无法避免)
  4. 应用重构模式以降低复杂度
  5. 持续跟踪复杂度改进情况
  6. 在 CI/CD 中强制执行复杂度阈值

必需工具

添加到 [dependency-groups] dev:"radon"、"lizard"、"xenon"、"wily"

  • radon:圈复杂度和可维护性指数
  • lizard:认知复杂度(更有利于提升可读性)
  • xenon:强制执行 CI/CD 阈值
  • wily:跟踪 git 历史中的趋势

权限:先运行 py-quality-setup,以配置 .claude/settings.local.json,添加所有必需的工具权限。

发现阶段

衡量复杂度
# Key commands
radon cc . -n C              # Functions with complexity ≥11
lizard -C 15 .               # Cognitive complexity warnings
radon mi . -n B              # Maintainability index <65
wily build .                 # Initialize tracking (one-time)
wily diff HEAD~10            # Compare trends
scc --by-file --ci           # Optional: code statistics (called 'sccount' on openSUSE)
阈值
  • 圈复杂度:达到 ≥C(11+)时重构
  • 认知复杂度:超过 15 时重构
  • 可维护性:低于 65 时重构
  • 每个代码文件的行数:显著超过 500 行时拆分,除非无法避免
手动识别模式

使用 Explore Agent 查找以下会增加复杂度的模式:

魔法数字和字符串:

  • 散布在代码中的数字字面量(0、1、-1 除外)
  • 用作键、阈值或配置的字符串字面量
  • 搜索多位数:grep -rE '\b[0-9]{2,}\b' --include='*.py'
  • 查找:费用计算、超时值、缓冲区大小、正则表达式模式

重复的字段操作:

  • 多个类似的 if 语句,用于检查或设置对象字段
  • 模式:if obj.field: obj.field = func(obj.field)
  • 搜索在不同字段上执行类似操作、包含 5 行以上代码的函数
  • 候选项:配置加载、验证、序列化、字段清除

重复的复杂类型定义:

  • 多处使用相同的复杂类型注解
  • 示例模式:在多个函数/类中重复使用 Literal["a", "b", "c"] | None
  • 搜索:grep -r 'Literal\[' --include='*.py',然后查找重复项
  • 还包括:复杂联合类型、多次使用的嵌套泛型
  • 候选项:函数参数、返回类型、类属性、cast() 调用

较长的 if/elif 链:

  • 5 个以上执行类似操作的条件分支
  • 通常可以用查找表或多态替代

深层嵌套代码:

  • 缩进层级达到 4 层以上
  • 多层嵌套循环或条件语句

重构模式

提取函数

将复杂函数拆分为职责明确的子函数:

# BEFORE - 20+ lines, complexity: 15
def process_order(order: dict) -> bool:
    # Validation, payment, confirmation logic all mixed

# AFTER - Each function <5 lines, complexity: <5
def process_order(order: dict) -> bool:
    return is_valid_order(order) and process_payment(order) and complete_order(order)
守卫子句

用提前返回替代嵌套条件:

# BEFORE - Deep nesting
if user:
    if user.get("active"):
        if user.get("verified"):
            return True
return False

# AFTER - Guard clauses
if not user or not user.get("active") or not user.get("verified"):
    return False
return True
查找表

用字典替代 if/elif 链:

# BEFORE - Nested conditionals (complexity: 7)
if customer == "gold" and total > 1000: return 0.20
elif customer == "gold": return 0.15
# ... more branches

# AFTER - Lookup table (complexity: 2)
RATES = {("gold", "high"): 0.20, ("gold", "low"): 0.15, ...}
return RATES.get((customer, tier), 0.0)
提取魔法数字和字符串

将散落的字面量提取到配置类中:

# BEFORE - Magic numbers throughout code
if amount < 100: base_fee = 2.50
elif amount < 1000: base_fee = 5.00
if account_type == "premium": return base_fee * 0.5

# AFTER - Configuration class
class FeeConfig:
    TIER_SMALL = 100
    FEE_SMALL = 2.50
    PREMIUM_DISCOUNT = 0.5

if amount < FeeConfig.TIER_SMALL:
    base_fee = FeeConfig.FEE_SMALL

优点:单一事实来源、易于修改、自描述,并且可以从环境变量加载

替换重复的字段操作

用遍历字段名的循环替换重复的 if 语句:

# BEFORE - 15 similar if-statements (complexity: 9)
if config.email.smtp_host:
    config.email.smtp_host = substitute_secret(config.email.smtp_host, secrets)
if config.email.smtp_user:
    config.email.smtp_user = substitute_secret(config.email.smtp_user, secrets)
# ... 13 more similar lines

# AFTER - Loop over field list (complexity: 3)
email_fields = ["smtp_host", "smtp_user", "smtp_password", "smtp_from"]
for field in email_fields:
    if value := getattr(config.email, field, None):
        setattr(config.email, field, substitute_secret(value, secrets))

模式:在验证、字段清除、转换和序列化中使用 getattr/setattr 循环

提取重复的复杂类型定义

使用 TypeAlias 替换重复的复杂类型注解:

# BEFORE - Type repeated 8 times across files
cache_mode: Literal["use", "only", "refresh"] | None
# ... used in 8 different functions, classes

# AFTER - Define once, use everywhere
CacheMode = Literal["use", "only", "refresh"]
CacheModeOptional = CacheMode | None

cache_mode: CacheModeOptional

放置位置:

  • 模块级:在单个模块中使用的类型
  • types.py:整个项目使用的类型
  • __init__.py:整个包导出的类型

优点:遵循 DRY 原则、名称更具语义、易于修改、减少拼写错误

验证清单

  • radon cc . -n C 未报告复杂度 ≥C(11+)的函数
  • lizard -C 15 . 未报告认知复杂度警告
  • radon mi . -n B 未报告可维护性指数 <65 的模块
  • 代码文件均不超过 500 行(除非无法避免)
  • 已运行 wily build . 初始化跟踪
  • 重构后所有测试均通过
  • 代码覆盖率保持不变或有所提高

示例

示例:降低复杂度的工作流

1. Measure: radon cc . -n C; lizard -CCN 15 .
2. Found: handlers.py:process_data (complexity D: 25)
3. Apply patterns: Extract functions, guard clauses, lookup tables
4. Result: 4 functions with complexity A-B
5. Track: wily diff HEAD~1 shows 20-point reduction

示例:应用专用模式

# Magic numbers: Search with grep, extract to config class
if amount < 100: fee = 2.50  # BEFORE
if amount < FeeConfig.TIER_SMALL: fee = FeeConfig.FEE_SMALL  # AFTER

# Repetitive fields: Replace 15 if-statements with loop
for field in ["smtp_host", "smtp_user", ...]:
    if value := getattr(config, field, None):
        setattr(config, field, process(value))

# Complex types: Extract repeated Literal types to TypeAlias
CacheMode = Literal["use", "only", "refresh"]  # Used 8 times → defined once

相关技能

  • 前置条件:py-quality-setup(工具配置)、py-test-quality(重构前的安全保障)
  • 前期清理:py-code-health(先移除无用代码,减少干扰)
  • 强制执行:py-git-hooks(将复杂度检查添加到 pre-commit)

评论

支持 Markdown