如何正确创建自定义 SymPy Symbol 类(支持完整运算重载)

在 sympy 中创建真正可用的自定义符号类需继承 `expr` 并重写 `add`/`mul`/`pow` 的核心方法(如 `flatten` 和 `__new__`),而非仅继承 `symbol`;否则幂运算等复合操作仍会退化为原生类型。

SymPy 的核心代数运算(如 +、*、**)并非完全通过操作符重载(如 __pow__)驱动,而是由顶层工厂类(如 Add、Mul、Pow)统一调度并执行表达式规范化(如合并同类项、指数相加)。因此,单纯让 CustomSymbol 重写 __pow__ 是无效的——当执行 a**2 * a**3 时,SymPy 内部会先将整个乘积识别为 Mul,再调用 Pow.flatten 或直接构造 Pow(a, 5),完全绕过 CustomSymbol.__pow__。

✅ 正确做法是构建一套协同工作的自定义表达式家族

  1. 定义基类 CustomExpr:统一实现 __add__/__mul__/__pow__,强制返回自定义运算类;
  2. 子类化 Symbol + CustomExpr:获得符号语义与自定义行为双重能力;
  3. 重写 CustomAdd/CustomMul/CustomPow 的关键方法

    • flatten(cls, seq):复制 SymPy 源码,将所有 Add/Mul/Pow 替换为对应自定义类(注意处理系数排序、非交换乘法等细节);
    • __new__(仅 CustomPow):缓存并确保返回 CustomPow 实例;
    • _eval_subs(self, old, new):返回 None,防止默认替换逻辑将自定义类“降级”为原生类;
  4. 全局替换运算符别名(可选但推荐)

    Add = CustomAdd
    Mul = CustomMul
    Pow = CustomPow

    这能确保后续所有 SymPy 内部构造(如 expand()、simplify())也使用你的类——否则它们仍会生成原生类型。

⚠️ 注意事项:

造好物

一站式AI造物设计平台

下载

  • 不要试图只覆盖 __pow__ 或 __mul__:SymPy 的 Mul 构造器会跳过实例方法,直接调用 Mul.flatten;
  • flatten 方法极其关键:它负责归并 a**2 * a**3 → a**5,若未重写,结果必为 Pow 而非 CustomPow;
  • 所有自定义类必须继承 CustomExpr(而非仅 Expr),以保证运算符重载链完整;
  • 若需 expand()、rewrite() 等高级功能,必须为每个自定义类实现对应的 _eval_expand_* 或 _eval_rewrite 方法,否则它们会回退到原生逻辑并破坏类型一致性。

下面是一个最小可行示例(已精简关键逻辑,生产环境请严格按 SymPy 源码补全 flatten):

from sympy import Expr, Symbol, Add, Mul, Pow, S
from sympy.core.add import _addsort
from sympy.core.mul import _mulsort, _keep_coeff

class CustomExpr(Expr):
    def __add__(self, other): return CustomAdd(self, other)
    def __radd__(self, other): return CustomAdd(other, self)
    def __mul__(self, other): return CustomMul(self, other)
    def __rmul__(self, other): return CustomMul(other, self)
    def __pow__(self, other): return CustomPow(self, other)

class CustomSymbol(CustomExpr, Symbol):
    def __new__(cls, name, **kwargs):
        return Symbol.__new__(cls, name, **kwargs)

class CustomAdd(CustomExpr, Add):
    @classmethod
    def flatten(cls, seq):
        # 复制 sympy.core.add.Add.flatten 源码,将内部 Add→CustomAdd, Mul→CustomMul, Pow→CustomPow
        from sympy.core.add import _addsort
        terms = []
        for x in seq:
            if isinstance(x, cls):
                terms.extend(x.args)
            else:
                terms.append(x)
        # ...(省略归并常数、排序等完整逻辑)
        coeff, nonnumber = S.Zero, []
        for t in terms:
            if t.is_Number:
                coeff += t
            else:
                nonnumber.append(t)
        if coeff is S.Zero and not nonnumber:
            return [S.Zero], []
        if coeff is S.Zero:
            newseq = nonnumber
        else:
            newseq = [coeff] + nonnumber
        _addsort(newseq)  # 排序
        return newseq, {}

    def _eval_subs(self, old, new): return None

class CustomMul(CustomExpr, Mul):
    @classmethod
    def flatten(cls, seq):
        # 同理:复制 Mul.flatten,替换为 CustomMul/CustomAdd/CustomPow
        ...
    def _eval_subs(self, old, new): return None

class CustomPow(CustomExpr, Pow):
    def __new__(cls, b, e, evaluate=None):
        # 复制 Pow.__new__,确保返回 CustomPow
        if evaluate is None:
            evaluate = global_parameters.evaluate
        if evaluate:
            # ...(标准求值逻辑)
            pass
        return Expr.__new__(cls, b, e)

    def _eval_subs(self, old, new): return None

# 全局启用(关键!)
Add = CustomAdd
Mul = CustomMul
Pow = CustomPow

# 验证
a = CustomSymbol('a')
x = a**2 * a**3
print(type(x))  # 
print(x)        # a**5

总结:SymPy 的设计决定了自定义符号必须是“生态级”扩展——不是单点重载,而是构建与 Symbol/Add/Mul/Pow 深度耦合的平行类体系。虽然工作量较大,但这是唯一能保证符号计算全程保持自定义语义(如附加元数据、特殊求值规则)的稳健方案。

相关推荐:

微软 Win11 效率神器 PowerToys 将改进“唤醒”功能,可自定义时长、绑定特定程序

7 月 6 日消息,微软 Win11 效率神器 PowerToys 有一个名为“唤醒”的工具,可以临时覆盖系统的睡眠机制,而无需更改操作系统电源计划。这个“唤醒”工具目前支持无限期保持唤醒、保持唤醒时间间隔、保持唤醒状态直到过期。而根据 PowerToys 在 GitHub 上新发布的一项提案,“唤醒”工具即将迎来重大改进。提案概述了重新设计的“唤醒”弹出菜单,旨在让用户无需通过“设置”应用或系统...

我的世界怎么设置自定义按键

我的世界设置自定义按键方法:1、点击“Esc”就会出现4个按键;2、点击“设置”这个按键;3、点击“键盘和鼠标”,你就可以在下图右侧窗口如图的位置自定义按键。 详细答案: 1、点击“Esc”就会出现4个按键,如图: 2、点击“设置”这个按键,如图: 3、点击“键盘和鼠标”,你就可以在下图右侧窗口如图的位置自定义按键。 本文内容来源于互联网,如有侵权请联系删除。

五笔打字练习软件键盘布局不习惯?自定义键位映射与个性化设置【技巧】

五笔练习软件键位不适应需重映射:金山打字通2025版支持图形化拖拽或编辑keymap文件;极点五笔插件提供三种预设布局快捷切换;系统级可用PowerToys键盘管理器全局重映射。 五笔打字练习软件键盘布局不习惯,导致击键错误率高、肌肉记忆混乱,甚至放弃练习——这不是输入法问题,而是键位映射未适配你手指的自然落点和旧有习惯。 确认当前五笔练习软件是否支持键位重映射 打开软件主界面,点击右上角齿轮图标...

夸克浏览器怎么强行切换默认搜索引擎为无广告纯净版的谷歌搜索_夸克搜索源底层自定义配置替换

可通过五种方法将夸克默认搜索引擎设为无广告纯净谷歌:一、关闭AI搜索增强以解锁谷歌选项;二、地址栏输入“g+关键词”跳转至含nfpr=1&udm=2参数的纯净谷歌页;三、自定义添加含该参数的谷歌搜索引擎并设为默认;四、启用网页智能保护过滤干扰广告;五、用ADB命令重置配置强制恢复谷歌入口。 如果您在使用夸克浏览器时希望将默认搜索引擎强制切换为无广告、纯净排版的谷歌搜索,但发现常规设置中仅显...

五笔打字练习软件如何导入自定义文章?针对性练习工作常用稿件【方法】

金山打字通支持导入本地TXT文件练习五笔:进入【五笔打字】→【文章练习】→【课程选择】选【自定义课程】→用【导入txt文章】或复制粘贴方式加载文件→命名保存即可。 想用五笔打字练习软件练自己写的会议纪要、合同条款或日报模板,但找不到导入入口?金山打字通支持直接加载你本地的TXT文档,无需转换格式,也不用一行行粘贴。 进入文章练习模块 启动金山打字通→点击顶部菜单【五笔打字】→在二级界面中找到并点击...

UC浏览器搜索引擎怎么添加自定义网址_UC浏览器搜索增强设置

必须手动注入URL模板才能启用知乎等垂直搜索服务:先在外部浏览器中通过真实搜索反推出含{searchTerms}的标准化URL模板,再进入UC设置→搜索引擎→添加并填写名称、域名、模板,最后设为默认并验证可用性。 想在UC浏览器地址栏直接搜知乎、微信搜一搜或DuckDuckGo,但内置列表里找不到?必须手动注入URL模板才能启用这些垂直搜索服务,否则输入关键词后只会跳转到默认引擎结果页。 提取目标...

p7华为怎么设置铃声?p7华为自定义来电铃声方法

p7华为设来电铃声需三步:1.将MP3/M4A/OGG格式音频(≤30秒)存入内部存储根目录的【Ringtones】文件夹;2.设置→声音和振动→来电铃声→切换至“本地铃声”页选择;3.双卡用户可分别点击“卡1”“卡2”设置,或用文件管理App长按音频→“设置为铃声”快捷操作。 华为P7用户想把喜欢的音乐设成来电铃声,但找不到入口或设置后不生效,需要明确路径和文件存放位置才能成功替换系统默认铃声。...

宙斯浏览器如何开启CPU加速_宙斯浏览器提升复杂网页运算速度

宙斯浏览器卡顿可通过启用多线程JavaScript、禁用V8内存压缩、绕过脚本超时限制及绑定高性能CPU核心解决。具体包括:在zeus://flags中启用webassembly-threads等三项实验功能;禁用v8-memory-compression;开启“允许长时间运行脚本”;将进程优先级设为“高于正常”,并切换系统电源模式为“高性能”,BIOS中关闭SpeedStep/Cool’n’Qu...