import time
gcount = 0
xdef = {}
# 定义成 def func(t=set(), x={}, dep=0): 也是同样的输出。
def func(t=set(), x=xdef, dep=0):
global gcount
gcount += 1
t.add(gcount)
x[gcount] = gcount
print(t, x)
if dep < 3:
func(t, x, dep+1)
if __name__ == "__main__":
func()
time.sleep(1)
func()
exit(0)
输出是:
{1} {1: 1}
{1, 2} {1: 1, 2: 2}
{1, 2, 3} {1: 1, 2: 2, 3: 3}
{1, 2, 3, 4} {1: 1, 2: 2, 3: 3, 4: 4}
{1, 2, 3, 4, 5} {1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
{1, 2, 3, 4, 5, 6} {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6}
{1, 2, 3, 4, 5, 6, 7} {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}
{1, 2, 3, 4, 5, 6, 7, 8} {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8}
瞬间感觉以前的代码貌似有很多 bug。
问题出在你的代码中使用了可变的默认参数,如 t=set() 和 x=xdef 。在 Python 中,可变的默认参数(比如 set 和 dict )是会在函数调用间共享的,这意味着在递归调用或者多次调用同一函数时,这些对象会被修改并持续累积,导致意想不到的行为。
**第一次调用 func() ** :第一次调用时, t 和 x 作为默认参数会被初始化为空集合和空字典, gcount 会在递归中递增, t 和 x 会按预期被修改。
**第二次调用 func() ** :你在 time.sleep(1) 后再次调用 func() ,此时 t 和 x 还是上一次调用时的对象(它们是可变的),导致它们继续累积之前的值。所以输出就变成了从第一次调用继续递增的结果。
默认参数是在函数定义时计算的,而不是在调用时。这就导致了 t 和 x 在多次调用中会共享同一个实例。
要避免这个问题,可以将默认参数设为 None ,然后在函数内部判断是否为 None ,如果是,则创建新的对象。这样每次调用时就会有新的 set 和 dict 实例,不会共享。
修改后的代码如下:
import time
gcount = 0
xdef = {}
def func(t=None, x=None, dep=0):
global gcount
if t is None:
t = set()
if x is None:
x = {}
gcount += 1
t.add(gcount)
x[gcount] = gcount
print(t, x)
if dep < 3:
func(t, x, dep+1)
if __name__ == "__main__":
func()
time.sleep(1)
func()
exit(0)
** t=None 和 x=None ** :将默认参数设为 None ,这样每次调用时, t 和 x 都是新的对象,不会与上一次的调用共享。
在函数内部 :检查 t 和 x 是否为 None ,如果是,就创建新的集合和字典。
执行修改后的代码,输出应该是这样的:
{1} {1: 1}
{1, 2} {1: 1, 2: 2}
{1, 2, 3} {1: 1, 2: 2, 3: 3}
{1, 2, 3, 4} {1: 1, 2: 2, 3: 3, 4: 4}
{5} {5: 5}
{5, 6} {5: 5, 6: 6}
{5, 6, 7} {5: 5, 6: 6, 7: 7}
{8, 5, 6, 7} {5: 5, 6: 6, 7: 7, 8: 8}
这样,每次调用 func() 时,都会用新的 t 和 x ,不会出现跨调用的共享问题,输出也符合预期。