0

0

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

霞舞

霞舞

发布时间:2026-01-14 16:36:02

|

801人浏览过

|

来源于php中文网

原创

如何正确创建自定义 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())也使用你的类——否则它们仍会生成原生类型。

⚠️ 注意事项:

Tome
Tome

先进的AI智能PPT制作工具

下载
  • 不要试图只覆盖 __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))  # <class '__main__.CustomPow'>
print(x)        # a**5

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

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1567

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

241

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

150

2025.10.17

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1567

2023.10.24

Go语言中的运算符有哪些
Go语言中的运算符有哪些

Go语言中的运算符有:1、加法运算符;2、减法运算符;3、乘法运算符;4、除法运算符;5、取余运算符;6、比较运算符;7、位运算符;8、按位与运算符;9、按位或运算符;10、按位异或运算符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

241

2024.02.23

php三元运算符用法
php三元运算符用法

本专题整合了php三元运算符相关教程,阅读专题下面的文章了解更多详细内容。

150

2025.10.17

Go语言实现运算符重载有哪些方法
Go语言实现运算符重载有哪些方法

Go语言不支持运算符重载,但可以通过一些方法来模拟运算符重载的效果。使用函数重载来模拟运算符重载,可以为不同的类型定义不同的函数,以实现类似运算符重载的效果,通过函数重载,可以为不同的类型实现不同的操作。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

196

2024.02.23

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

38

2026.03.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Java 教程
Java 教程

共578课时 | 81.2万人学习

国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号