在定义一个函数时,有的时候会添加默认参数。如
def fun(a = 0):
pass
但默认参数倘若不注意会跳进一个较大的坑,如
## 设置个标签 flag
def fun(L = []):
L.append(1)
return L
a = fun()
b = fun()
问此时a, b的值是什么。倘若认为此时的 L 为局部变量,则很容易答出a = [1], b = [1]
,但是应该是 a = [1, 1], b = [1, 1]
这一特性在Python的官方文档中有特别说明,位置为:入门教程/4.7.1默认参数值
文档的大意为:
默认参数在函数被定义的同时也被定义。
举个例子更加好理解:
i = 5
def fun(num = i):
print(num)
i = 6
fun()
#=====run=====
5
对前面flag中的函数可以这样改编
l = []
def fun(L = l):
L.append(1)
return L
a = fun()
b = fun()
在这里将上面默认参数的值,也就是空列表具体赋给变量 l(小写L)。
接下来是python几个特性的融合:
首先默认参数在函数被定义的时候被定义,所以 L 在函数被定义的时候得到空列表的指针。
但是由于在python中可变对象的传递都是传递指针,所以在两次函数调用后 l, L, a, b 四个变量指向的都是同一个内存地址。也就是:
那么无论是谁对这片内存空间进行操作,四个指向此内存空间的变量都会发生改变(关于可变对象的特性建议学习c语言的指针进行更加深刻的理解,在此之前可以当作知识点背诵)
如果查看 l 的值可以发现 l 的值也和a, b一样。
那么回到 flag 里的函数
def fun(L = []):
L.append(1)
return L
a = fun()
b = fun()
道理是完全一样的:
首先默认参数在函数被定义的时候定义,所以 python 在内存中创建了一个空列表,并且让 L 指向空列表
调用两次函数后 L, a, b 指向的是同一片内存空间
不同的是因为并没有在全局变量中定义一个变量(如 l)指向这片空间,所以在主函数中是无法直接访问这片空间的。
备注:虽然说 L 也是指向这片内存空间的,但是因为 L 的作用域只在函数内部,所以在主函数中是没办法通过 L 访问这片内存空间。但是可以在函数内部指向此内存空间。