Julia中从已有结构体创建新实例遇StackOverflowError及字段未更新问题
问题分析与解决
为什么b字段未更新?
用@with_kw定义结构体时,b = (; b1 = a1, b2 = a2)这个默认值仅在首次创建实例时计算一次。当基于已有实例anobject生成新实例object2时,Parameters包会直接复用原实例的b字段值,不会重新根据新的a1计算,导致b.b1仍保留旧值1,而非新设置的5。
为什么自定义构造函数触发StackOverflowError?
你编写的构造函数:
A(a::A; a1 = 0, kw...) = A(a; a1 = a1, b = (; a.b..., b1 = a1), kw...)
其中A(a; ...)会直接调用你刚定义的这个A(a::A; ...)方法,形成无限递归,最终触发栈溢出。
正确实现方法
方法1:手动解构实例并重新计算b
直接拆解已有实例的字段,结合新参数生成新实例,避免递归调用:
using Parameters @with_kw struct A a1 = 0 a2 = 0 b = (; b1 = a1, b2 = a2) end # 定义基于A实例的构造函数 function A(a::A; a1=a.a1, a2=a.a2, kw...) # 根据当前a1/a2计算新的b new_b = (; b1 = a1, b2 = a2) # 调用默认关键字构造函数传入所有参数 A(; a1=a1, a2=a2, b=new_b, kw...) end
测试验证:
anobject = A(; a1=1, a2=3) object2 = A(anobject; a1=5) # 此时object2.a1=5,object2.b.b1=5,object2.b.b2=3,符合预期
方法2:让b始终自动关联a1/a2
如果希望b字段实时同步a1、a2的变化,可以将b定义为计算属性,而非存储字段:
using Parameters @with_kw struct A a1 = 0 a2 = 0 end # 给A添加计算属性b Base.getproperty(a::A, sym::Symbol) = sym === :b ? (; b1=a.a1, b2=a.a2) : getfield(a, sym)
这种方式下,每次访问b都会根据当前的a1和a2实时生成,创建新实例时自然同步更新:
anobject = A(; a1=1, a2=3) object2 = A(anobject; a1=5) # object2.b.b1=5,object2.b.b2=3,无需手动处理构造函数
注意:此方式下b不是结构体的存储字段,每次访问都会重新生成NamedTuple,适合对性能要求不高、需要自动同步的场景。
内容的提问来源于stack exchange,提问作者Carlos Payá
相关产品推荐
相关产品推荐

