Lambda函数使用copy/deepcopy无效?分段多项式调用报错排查
复用coeffs变量导致分段多项式函数维度不匹配错误
触发错误的最小代码示例
import numpy as np from copy import deepcopy x = np.array([0. , 2.0943951, 4.1887902]) coeffs = np.array([[-0.08952269, 1.59581565, -0.55240446], [ 2.33359441, -0.71809073, 0. ]]) quadratic_spline = piecewise_polynomial(coeffs, x) print(quadratic_spline(3)) coeffs = np.array([[ 0.06200785, 0.60701535, -0. , -0.06631379], [-1.15644773, 2.3523244 , -0.83332369, 0.06631379]]) print(quadratic_spline(3))
错误输出
0.17932221999999998 --------------------------------------------------------------------------- ValueError Traceback (most recent call last) /var/lib/condor/execute/dir_1389034/ipykernel_1403467/1828880745.py in <module> 10 [-1.15644773, 2.3523244 , -0.83332369, 0.06631379]]) 11 ---> 12 print(quadratic_spline(3)) <string> in <lambda>(x, order, xs) ValueError: operands could not be broadcast together with shapes (3,) (4,)
分段多项式函数定义
def piecewise_polynomial(coeffs, xs): """ 给定单变量分段多项式的系数和每段的区间边界,构造对应的函数。 多项式的基函数为 1, x, x², ..., xⁿ, ... 如果xs的长度过高,解析器无法处理复杂语法,函数会失效。 输入: coeffs:np.2darray xs:np.array 返回: piecewise_polynomial:function float to float """ coeffs = deepcopy(coeffs) function_str = f'lambda x, order={coeffs.shape[1]}, xs=np.{repr(xs)}: np.sum(np.array([x**n for n in range(order)])*coeffs[0]) if xs[0] <= x <= xs[1]' for i, x in enumerate(xs[1:-1], start=1): function_str += f' else (np.sum(np.array([x**n for n in range(order)])*coeffs[{i}]) if xs[{i}] < x <= xs[{i+1}]' function_str += f" else None{')'*(len(xs)-2)}" return eval(function_str)
已尝试的解决方法
quadratic_spline = piecewise_polynomial(coeffs.copy(), x)quadratic_spline = piecewise_polynomial(copy.deepcopy(coeffs), x)quadratic_spline = copy.deepcopy(piecewise_polynomial(coeffs, x))
以上方法及组合均未解决问题,只有不复用coeffs变量名才能避免错误。期望两次输出0.17932221999999998。
错误原因
问题核心在于eval生成的lambda函数并未固化创建时的coeffs值。虽然函数内执行了coeffs = deepcopy(coeffs),但生成的lambda字符串中直接引用了全局的coeffs变量,而非当时的副本值。当后续全局coeffs被重新赋值为4列数组后,lambda调用时仍使用原来的order=3(来自第一次创建时的coeffs.shape[1]),导致基函数数组形状(3,)与新coeffs的(4,)无法广播相乘,触发维度不匹配错误。
解决方案
方案1:将coeffs值嵌入lambda字符串
修改函数,把coeffs的具体值通过repr嵌入到lambda定义中,让lambda内部使用创建时的coeffs副本,而非全局变量:
def piecewise_polynomial(coeffs, xs): coeffs = deepcopy(coeffs) coeffs_repr = repr(coeffs) function_str = f'lambda x, order={coeffs.shape[1]}, xs=np.{repr(xs)}, coeffs=np.{coeffs_repr}: np.sum(np.array([x**n for n in range(order)])*coeffs[0]) if xs[0] <= x <= xs[1]' for i, x_val in enumerate(xs[1:-1], start=1): function_str += f' else (np.sum(np.array([x**n for n in range(order)])*coeffs[{i}]) if xs[{i}] < x <= xs[{i+1}]' function_str += f" else None{')'*(len(xs)-2)}" return eval(function_str)
方案2:改用闭包替代eval(推荐)
完全避免使用eval,用闭包封装状态,更安全可靠:
def piecewise_polynomial(coeffs, xs): coeffs = deepcopy(coeffs) xs = deepcopy(xs) order = coeffs.shape[1] def spline_func(x): # 查找x所在的区间索引 idx = np.searchsorted(xs, x, side='right') - 1 idx = np.clip(idx, 0, len(coeffs)-1) # 计算多项式值 basis = np.array([x**n for n in range(order)]) return np.sum(basis * coeffs[idx]) return spline_func
闭包会保留函数创建时的coeffs、xs和order副本,后续修改全局变量不会影响内部引用,彻底解决问题。
内容的提问来源于stack exchange,提问作者Yodo
相关产品推荐
相关产品推荐

