def extendlist(val, aaa=[]):
print("运行前aaa的值是:",aaa,"aaa的id是:",id(aaa))
aaa.append(val)
print("运行后aaa的值是:",aaa)
return aaa
list1 = extendlist(10)
print("list1的值和id是:",list1,id(list1))
list2 = extendlist(123, [])
print("list2的值和id是:",list2,id(list2))
list3 = extendlist('a')
print("list3的值和id是:",list3,id(list3))
print(list1,list2,list3)
print(id(list1),id(list3),id(list2))
运行结果如下:
运行前aaa的值是: [] aaa的id是: 99826056
运行后aaa的值是: [10]
list1的值和id是: [10] 99826056
运行前aaa的值是: [] aaa的id是: 99774216
运行后aaa的值是: [123]
list2的值和id是: [123] 99774216
运行前aaa的值是: [10] aaa的id是: 99826056
运行后aaa的值是: [10, 'a']
list3的值和id是: [10, 'a'] 99826056
[10, 'a'] [123] [10, 'a']
99826056 99826056 99774216
预期结果与实际不一致,原因是当我们调用函数时,如果有传递参数,则使用传递的参数,如果没有传递参数,就使用默认参数。而使用默认参数的时候,使用的是同一个,也就是保存的上一个默认参数值,通过最后的id可以发现,第三次调用参数时,默认参数的值是[10],而不是[].
为什么是同一个?
看如下代码:
def bar(args):
args.append(1)
b = []
print(b)# 输出:[]
print(id(b)) # 输出:4324106952
bar(b)
print(b) # 输出:[1]
print(id(b)) # 输出:4324106952
Python 函数中,参数的传递本质上是一种赋值操作,而赋值操作是一种名字到对象的绑定过程,内存中绑定情况如下:
因为函数定义好之后,默认参数 aaa就会指向(绑定)到一个空列表对象,每次调用函数时,都是对同一个对象进行 append 操作。
详细请看:Python 函数中,参数是传值,还是传引用?
可以看到第一次调用时,list1确实是[10],但是当list3调用时,由于没有传递参数,使用上一次的值,所以此时aaa!=[],而是aaa=[10]
调用完成后,返回list3=[10,a],list1也是指向aaa的,所以此时list1的值会发生变化为list1=[10,a]
需要牢记的是默认参数不要用可变对象!!!
如果非要这样使用,可以在将aaa指向不可变对象None,在每次进行运算前进行判断:
def extendlist(val, aaa=None):
if aaa==None:
aaa=[]
print("运行前aaa的值是:",aaa,"aaa的id是:",id(aaa))
aaa.append(val)
print("运行后aaa的值是:",aaa)
return aaa
list1 = extendlist(10)
print("list1的值和id是:",list1,id(list1))
list2 = extendlist(123, [])
print("list2的值和id是:",list2,id(list2))
list3 = extendlist('a')
print("list3的值和id是:",list3,id(list3))
print(list1,list2,list3)
print(id(list1),id(list3),id(list2))
运行前aaa的值是: [] aaa的id是: 100677960
运行后aaa的值是: [10]
list1的值和id是: [10] 100677960
运行前aaa的值是: [] aaa的id是: 100626120
运行后aaa的值是: [123]
list2的值和id是: [123] 100626120
运行前aaa的值是: [] aaa的id是: 35127624
运行后aaa的值是: ['a']
list3的值和id是: ['a'] 35127624
[10] [123] ['a']
100677960 35127624 100626120