You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何构建结构规范的自定义Python信号处理模块

信号处理模块代码设计方案

1. 现有代码结构评估

现存问题

  • 可扩展性不足:当前逻辑完全绑定一阶低通场景,order参数仅做了等于1的分支判断,后续支持高阶滤波器、其他类型滤波器时必须直接修改现有类代码,极易引入回归bug;参数校验、传递函数计算、绘图、滤波逻辑全部耦合在同一个类中,修改任意一块逻辑都可能影响其他功能。
  • 可测试性极差:
    • 硬依赖matplotlib,执行核心计算逻辑也会导入绘图库,单元测试时无法脱离GUI环境运行
    • propagate方法直接调用未定义的impulse_response方法,代码实际运行会直接抛出AttributeError
    • 无任何参数合法性校验,传入截止频率高于奈奎斯特频率、采样率为0、阶数小于1等非法参数时,初始化阶段不会拦截,直到计算环节才会抛出无明确指向的错误
    • 全部使用Python原生list做数值计算,无统一的数值精度控制,测试时断言结果难度高
  • 明确设计缺陷:
    • 命名不符合PEP8规范:类名使用了函数/变量级别的下划线命名sim_lowpass,正确类名应使用大驼峰格式
    • 方法边界混乱:私有方法_transfer_func和公开方法transfer_func命名几乎一致,调用时极易混淆
    • 强依赖无隔离:matplotlib作为非核心计算依赖没有做懒加载,仅需要滤波结果的用户也必须安装绘图库
    • 滤波逻辑错误:频域滤波直接将输入序列和传递函数做zip对位相乘,既没有做FFT/IFFT变换,也没有处理序列长度不一致的截断问题,无法得到正确的滤波结果

需补充考量的工程指标

  • 输入合法性校验:初始化、公开方法入口处拦截非法参数,抛出明确可读的异常
  • 依赖隔离:重型、可选依赖做懒加载,降低核心功能的安装门槛
  • 测试友好:核心计算逻辑完全独立于IO、绘图等副作用操作,可直接运行单元测试
  • 文档规范:所有公开类、方法补充标准docstring,说明参数取值范围、返回值含义、异常场景
  • 可扩展性:新增滤波器类型时不需要修改现有已稳定运行的代码

2. 高低通公共逻辑复用方案

采用模板方法模式实现逻辑复用,抽离通用滤波器基类,所有公共逻辑下沉到基类,不同滤波器仅需实现自身差异化的核心计算逻辑:

  • 基类BaseFilter统一实现:参数校验、公共属性存储、扫频生成传递函数序列、伯德图绘制、通用滤波流程、脉冲响应计算等所有高低通滤波器共有的逻辑
  • 低通、高通等具体滤波器子类,仅重写单频点传递函数计算这一个差异化方法即可,后续新增带通、带阻滤波器也只需要新增子类,不需要修改任何现有代码

简化实现示例:

import math
from typing import List, Union, Optional

class BaseFilter:
    def __init__(self, cutoff: int, order: int, fs: int) -> None:
        # 统一参数校验
        if fs <= 0:
            raise ValueError("采样频率fs必须大于0")
        nyq = 0.5 * fs
        if not 0 < cutoff < nyq:
            raise ValueError(f"截止频率必须处于0到{nyq}Hz区间内")
        if order < 1:
            raise ValueError("滤波器阶数必须≥1")
        self.fs = fs
        self.nyq = int(nyq)
        self.cutoff = cutoff
        self.order = order

    def _point_transfer_gain(self, f: float) -> float:
        # 子类必须实现的单频点增益计算方法
        raise NotImplementedError("具体滤波器类必须实现单频点增益计算逻辑")

    def get_transfer_func(self, freq_points: Optional[Union[int, List[float]]] = None) -> List[float]:
        # 公共扫频逻辑,支持自定义频率点
        if freq_points is None:
            freq_points = list(range(self.nyq))
        return [self._point_transfer_gain(f) for f in freq_points]

    def bode_plot(self, tr_func: Optional[List[float]] = None) -> None:
        # 懒加载matplotlib,仅调用绘图时才导入依赖
        from matplotlib import pyplot as plt
        if tr_func is None:
            tr_func = self.get_transfer_func()
        # 伯德图绘制逻辑
        fig, (ax1, ax2) = plt.subplots(2, 1, constrained_layout=True, figsize=(8,5))
        # 补充幅度转dB、相位计算逻辑
        ax1.plot(list(range(self.nyq)), [20*math.log10(max(g, 1e-10)) for g in tr_func])
        ax1.set_xscale('log')
        ax1.set_ylabel('Magnitude (dB)')
        ax1.grid(True)
        ax2.set_xscale('log')
        ax2.set_xlabel('Frequency (Hz)')
        ax2.set_ylabel('Phase (deg)')
        ax2.grid(True)
        fig.suptitle('Bode Plot', fontsize=16)

    def propagate(self, x: List[float]) -> List[float]:
        # 公共滤波逻辑,后续补全FFT/IFFT、加窗、长度对齐处理
        raise NotImplementedError("滤波逻辑待补全")

class LowPassFilter(BaseFilter):
    def _point_transfer_gain(self, f: float) -> float:
        return 1/(1+(f/(2*math.pi*self.cutoff)))

class HighPassFilter(BaseFilter):
    def _point_transfer_gain(self, f: float) -> float:
        # 一阶高通增益可直接复用低通计算结果,无需重复编写公式
        return 1 - 1/(1+(f/(2*math.pi*self.cutoff)))

3. 包结构与文件命名规范

遵循通用Python开源包结构,核心源码、测试代码、示例代码分离,推荐结构如下:

sigproc/  # 包根目录,全小写下划线命名,替换为你自己的包名
├── __init__.py  # 顶层导出配置,可将LowPassFilter、HighPassFilter直接导出到包命名空间
├── filters/  # 滤波器子模块
│   ├── __init__.py
│   ├── base.py  # 存放BaseFilter基类
│   ├── iir.py   # 存放IIR类滤波器实现(低通、高通、带通、带阻),后续FIR滤波器可单独放fir.py
│   └── plot.py  # 绘图逻辑复杂时可单独抽离,不耦合在滤波器类中
├── core/  # 存放FFT、窗函数、卷积等通用信号处理工具
├── tests/  # 单元测试目录,与源码完全分离
│   ├── test_lowpass.py
│   └── test_highpass.py
├── examples/  # 示例代码目录
│   └── lowpass_demo.py  # 你原来写在main函数里的测试逻辑移到这里
└── pyproject.toml  # 包安装、依赖配置文件

命名规则注意:

  • 所有文件名、目录名使用全小写+下划线分隔,不要使用sim_lowpass这类带无意义前缀、驼峰格式的命名
  • 类名使用大驼峰格式,比如LowPassFilter,方法、变量使用全小写+下划线格式
  • 不要将测试代码、示例demo代码放在核心源码目录下
  • 你当前编写的低通类,重命名为LowPassFilter后放在sigproc/filters/iir.py路径下即可,原文件头部的创建时间、作者信息不需要放在核心源码里,由版本控制工具记录即可。

4. 可落地优化建议

  • 数值计算向量化:将原生list、math计算替换为numpy实现,利用数组广播做批量计算,速度可提升数十倍,同时统一数值精度,减少手动循环引入的错误
  • 补全核心逻辑缺陷:伯德图的幅度值需要转为dB单位(20*log10(幅值)),补充相位计算逻辑;propagate方法补全FFT变换、频域相乘、IFFT逆变换、序列长度对齐的完整流程,不要直接对位相乘
  • 提升接口灵活性:传递函数计算支持用户自定义频率采样点;伯德图支持传入用户自定义的matplotlib坐标轴对象,方便将多个滤波器的响应曲线画在同一张图中对比
  • 可选依赖处理:将matplotlib设为可选依赖,用户安装核心包时不需要强制安装绘图库,调用绘图方法时如果检测到没装matplotlib,给出明确的安装提示即可
  • 补全类型标注:所有公开方法的参数、返回值都补充完整类型提示,方便IDE自动补全,也方便静态检查工具提前发现类型错误
  • 移除核心源码中的测试逻辑:原文件中写在if __name__ == '__main__'下的测试代码,要么移到examples目录做使用示例,要么改写成单元测试用例放在tests目录下

内容的提问来源于stack exchange,提问作者ilja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 20:48:19