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

SymPy自定义cSymbol类函数内声明影响外部实例问题求解

问题:SymPy自定义cSymbol类的属性被意外修改

我为SymPy自定义了cSymbol类,用来给已声明的符号添加属性,实现代码如下:

class cSymbol(sy.Symbol):
    def __init__(self,name,x,**assumptions):
        self.x = x 
        sy.Symbol.__init__(name,**assumptions)

但遇到一个问题:在函数内声明同名的cSymbol实例(比如名称为"a")时,会修改外部已声明的同名cSymbol实例的x属性:

def some_function():
    dummy = cSymbol("a",x=2)

a = cSymbol("a",x=1)
print(a.x)  # >> 1
some_function()
print(a.x)  # >> 2,预期应为1

原本以为函数内的变量都是局部的,不理解为什么会出现这种情况。除了使用不同名称,有没有办法避免这个问题?完整代码如下:

import sympy as sy

class cSymbol(sy.Symbol):
    def __init__(self,name,x,**assumptions):
        self.x = x 
        sy.Symbol.__init__(name,**assumptions)
        
def some_function():
    a = cSymbol("a",x=2)


if __name__ == "__main__":
    a = cSymbol("a",x=1)
    print(a.x)  # >> 1
    some_function()
    print(a.x)  # >> 2,预期应为1

问题原因

SymPy的Symbol类采用单例模式:同名且假设参数一致的符号会共享同一个实例。当你第二次创建名称为"a"的cSymbol时,SymPy不会生成新实例,而是直接返回之前已创建的那个实例。所以函数内对x属性的赋值,实际上是修改了外部那个实例的属性。

解决方法

方法1:重写__new__方法,强制生成新实例

SymPy的单例逻辑在__new__方法中实现,重写该方法可以跳过缓存,确保每次创建cSymbol都生成独立实例:

import sympy as sy

class cSymbol(sy.Symbol):
    def __new__(cls, name, x, **assumptions):
        # 直接调用父类__new__,跳过SymPy的单例缓存
        obj = super().__new__(cls, name, **assumptions)
        return obj
    
    def __init__(self, name, x, **assumptions):
        self.x = x
        super().__init__(name, **assumptions)

修改后,函数内的局部cSymbol实例和外部实例完全独立,不会互相影响。

方法2:自定义缓存键,区分带x属性的实例

如果需要保留SymPy的缓存逻辑,但希望不同x值的同名实例不共享,可以修改缓存键,把x属性加入缓存判定条件:

import sympy as sy
from sympy.core.symbol import _symbol_cache

class cSymbol(sy.Symbol):
    def __new__(cls, name, x, **assumptions):
        # 将x属性加入缓存键,确保只有名称、假设、x都相同时才共享实例
        cache_key = (cls, name, frozenset(assumptions.items()), x)
        if cache_key in _symbol_cache:
            return _symbol_cache[cache_key]
        obj = super().__new__(cls, name, **assumptions)
        _symbol_cache[cache_key] = obj
        return obj
    
    def __init__(self, name, x, **assumptions):
        self.x = x
        super().__init__(name, **assumptions)

这种方式下,同名但x值不同的cSymbol会被视为不同实例,不会互相覆盖属性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 12:20:50