Python中相同输入为何输出差异巨大?函数执行异常排查
问题现象
执行以下代码时,预期输出True,实际得到False:
a = cornerpoints(2) b = sntx(a) c = sntx(cornerpoints(2)) print(b == c)
同时,定义如下辅助函数时也出现异常:
def d(dim): a = cornerpoints(dim) b = sntx(a) return b
当dim=2时,b和c的具体输出如下:
print(b)
[\left{1<=t<=2:\left(2\right)\left(t-1\right)+-1,\left{t<2:-1,\left{2<=t<=3:\left(2\right)\left(t-2\right)+-1,\left{t<3:-1,\left{t<4:1,0\right}\right}\right}\right},
\left{0<=t<=1:\left(2\right)\left(t-0\right)+-1,\left{t<2:-1,\left{t<3:1,\left{3<=t<=4:\left(2\right)\left(t-3\right)+-1,\left{t<4:-1,0\right}\right}\right}\right}
print(c)
[\left{1<=t<=2:\left(2\right)\left(t-1\right)+-1,\left{3<=t<=4:\left(2\right)\left(t-3\right)+-1,\left{t<4:-1,\left{4<=t<=5:\left(2\right)\left(t-4\right)+-1,\left{6<=t<=7:\left(2\right)\left(t-6\right)+-1,\left{t<7:-1,\left{8<=t<=9:\left(-2\right)\left(t-8\right)+1,\left{t<10:1,\left{10<=t<=11:\left(-2\right)\left(t-10\right)+1,\left{t<12:1,\left{13<=t<=14:\left(2\right)\left(t-13\right)+-1,\left{t<14:-1,\left{14<=t<=15:\left(2\right)\left(t-14\right)+-1,\left{t<15:-1,\left{t<16:1,0\right}\right}\right}\right}\right}\right}\right}\right}\right}\right}\right},
\left{0<=t<=1:\left(2\right)\left(t-0\right)+-1,\left{2<=t<=3:\left(2\right)\left(t-2\right)+-1,\left{t<4:-1,\left{5<=t<=6:\left(-2\right)\left(t-5\right)+1,\left{t<7:1,\left{7<=t<=8:\left(2\right)\left(t-7\right)+-1,\left{9<=t<=10:\left(2\right)\left(t-9\right)+-1,\left{t<10:-1,\left{11<=t<=12:\left(-2\right)\left(t-11\right)+1,\left{t<12:1,\left{12<=t<=13:\left(2\right)\left(t-12\right)+-1,\left{t<14:-1,\left{t<15:1,\left{15<=t<=16:\left(2\right)\left(t-15\right)+-1,\left{t<16:-1,0\right}\right}\right}\right}\right}\right}\right}\right}\right}\right}\right}\right}
相关函数代码
cornerpoints函数
import copy def cornerpoints(dim,x=[],xs=[]): for i in [-1,1]: x.append(i) if len(x) == dim: xs.append(copy.copy(x)) else: cornerpoints(dim,x,xs) x.pop(-1) return xs
sntx函数
def sntx(a): G = nachbarn(a) U = indexUnterschied(G,a) dim = len(a[0]) k = [1 for i in range(dim)] # Count of opened Barckets n = 0 # Position of t s = ["\\left\\{" for sdf in range(dim)] for i in range(len(G)): for dimension in U[i]: # print(dimension,i) ind = U[i].index(dimension) vecI= G[i][ind] s[dimension] += lrp(a[i][dimension], a[vecI][dimension], n) + ",\\left\\{" n += 1 k[dimension] += 1 for j in range(dim): s[j] += f"t<{n}:{ecken[i][j]}" + ",\\left\\{" k[j] += 1 for j in range(dim): s[j] = s[j][:-7] s[j] += "0" for ikj in range(k[j]-1): s[j] += "\\right\\}" return s
注:sntx函数仅将信息转换为Desmos可识别的语法,仅修改字符串列表s,U和G为简单嵌套列表。
问题根源
cornerpoints的可变默认参数陷阱:
Python中,可变默认参数(比如x=[], xs=[])是在函数定义时初始化的,而非每次调用时。这意味着每次调用cornerpoints都会复用同一个x和xs列表,多次调用后xs会积累之前的结果,返回的角点列表越来越长,最终导致sntx处理的输入不一致。sntx中的未定义变量ecken:
代码中s[j] += f"t<{n}:{ecken[i][j]}"里的ecken未定义,推测是笔误,应为a[i][j]。如果ecken是全局变量,会导致每次调用sntx时引用全局状态,进一步加剧结果不一致。
修复方案
修复cornerpoints函数
将可变默认参数改为None,在函数内部初始化列表:
import copy def cornerpoints(dim, x=None, xs=None): # 初始化默认参数 if x is None: x = [] if xs is None: xs = [] for i in [-1,1]: x.append(i) if len(x) == dim: xs.append(copy.copy(x)) else: cornerpoints(dim, x, xs) x.pop(-1) return xs
修复sntx函数的笔误
将ecken[i][j]改为a[i][j]:
def sntx(a): G = nachbarn(a) U = indexUnterschied(G,a) dim = len(a[0]) k = [1 for i in range(dim)] # Count of opened Barckets n = 0 # Position of t s = ["\\left\\{" for sdf in range(dim)] for i in range(len(G)): for dimension in U[i]: ind = U[i].index(dimension) vecI= G[i][ind] s[dimension] += lrp(a[i][dimension], a[vecI][dimension], n) + ",\\left\\{" n += 1 k[dimension] += 1 for j in range(dim): # 修复笔误:ecken -> a s[j] += f"t<{n}:{a[i][j]}" + ",\\left\\{" k[j] += 1 for j in range(dim): s[j] = s[j][:-7] s[j] += "0" for ikj in range(k[j]-1): s[j] += "\\right\\}" return s
验证修复
修改后重新执行测试代码:
a = cornerpoints(2) b = sntx(a) c = sntx(cornerpoints(2)) print(b == c) # 现在输出True
内容的提问来源于stack exchange,提问作者ShittyHobbyDev

