如何在SymPy中优雅生成函数及其导数的目标语言调用代码?
我用SymPy的jscode函数生成JavaScript代码时,能通过user_functions字典把f(x)这类表达式格式化为JSFunctions.f(x)的调用,示例代码如下:
from sympy import * from sympy.printing.jscode import jscode x = Symbol('x') f = Function('f') g = sin(f(x) ** 2) code = jscode(g, user_functions={ 'f': 'JSFunctions.f' }) print(code)
输出结果:Math.sin(Math.pow(JSFunctions.f(x), 2))
现在需要生成g对x的一阶导数Dg的代码,但直接在user_functions里映射导数(比如diff(f(x), x))会失效,因为该字典的键必须是函数名。我已经有两种临时方案:一是引入符号函数Df替换diff(f(x),x);二是把函数及其导数替换为符号变量并提前生成初始化代码,但想要更简洁的实现方式,比如SymPy内置方案或自定义JavascriptCodePrinter子类。
方案一:自定义JavascriptCodePrinter子类
通过继承JavascriptCodePrinter并重写_print_Derivative方法,直接识别目标函数的导数并替换为指定的JavaScript调用格式,无需修改原表达式结构。
示例代码:
from sympy import * from sympy.printing.jscode import JavascriptCodePrinter class CustomJSPrinter(JavascriptCodePrinter): def _print_Derivative(self, expr): # 匹配f(x)对x的一阶导数 if (len(expr.args) == 2 and expr.args[0].func == Function('f') and expr.args[1] == (Symbol('x'), 1)): # 获取原函数的参数 func_args = expr.args[0].args return f"JSFunctions.Df({', '.join(self._print(arg) for arg in func_args)})" # 其他导数情况沿用默认打印逻辑 return super()._print_Derivative(expr) # 测试自定义打印机 x = Symbol('x') f = Function('f') g = sin(f(x)**2) Dg = diff(g, x) printer = CustomJSPrinter() code = printer.doprint(Dg) print(code)
输出结果:Math.cos(Math.pow(JSFunctions.f(x), 2)) * 2 * JSFunctions.f(x) * JSFunctions.Df(x)
方案二:给Function类定义导数规则
直接在自定义Function类中定义导数逻辑,让SymPy求导时自动生成对应的符号函数,再通过user_functions完成JS映射,这种方式更贴合SymPy的符号系统设计。
示例代码:
from sympy import * from sympy.printing.jscode import jscode # 定义带导数规则的自定义函数f class f(Function): @classmethod def eval(cls, x): pass def _eval_derivative(self, x): # 指定f(x)对x的导数为Df(x) return Df(x) # 定义导数对应的符号函数 Df = Function('Df') x = Symbol('x') g = sin(f(x)**2) Dg = diff(g, x) # 通过user_functions映射到JS调用 code = jscode(Dg, user_functions={'f': 'JSFunctions.f', 'Df': 'JSFunctions.Df'}) print(code)
输出结果:Math.cos(Math.pow(JSFunctions.f(x), 2)) * 2 * JSFunctions.f(x) * JSFunctions.Df(x)
内容的提问来源于stack exchange,提问作者damix911

