在 python 中定义函数,其参数可以使用多种不同的方式,其中包括 “默认值参数”类型,那么当作默认值的对象有什么限制和要求么?这里搞不好还真有坑! 接下来我们主要从两个角度来谈谈。
参数的默认值:
先复原需求
定义一个函数,为传入的列表(list)尾部添加一个“end”元素。
如:传入: [1, 2, 3]
输出: [1 ,2, 3, 'end']
实现代码:
def addend(lt=[]):
lt.append('end')
return lt
# 传入一个普通的列表,输出结果正确
lst = [1, 2, 3, 4]
print(addend(lst))
# 不传入任何参数,第二次调用,多输出了一个'end'
print(addend())
print(addend())
---------------------
输出:
[1, 2, 3, 4, 'end']
['end']
['end', 'end']
问题分析
先观察这个函数:
再看执行过程:
1. def addend(lt=[])
2. lst = [1,2,3,4]
3. print(addend(lst))
4. print(lst)
稍加变化
清楚了以上机制后,我们再稍微变化一下,函数定义处依旧使用默认参数形式,但是其默认的参数值不是一个空的列表了,而是有数据元素的列表([1,2])。
同样的道理,当调用这个参数的时候,只要传入了实参列表,不论形参值是什么,形参变量 lt 都指向了新传入的实参空间,并在新传入的空间内附加上 'end' 元素。
所以输出的结果和形参的值没有任何关系。
def addend(lt=[1, 2]):
lt.append('end')
return lt
lst = [1, 2, 3, 4]
print(addend(lst))
print(lst)
------------------------------
输出:
[1, 2, 3, 4, 'end']
[1, 2, 3, 4, 'end']
接下来继续分析不传入实参的情况
def addend(lt=[]):
lt.append('end')
return lt
print(addend())
print(addend())
---------------------
输出:
['end']
['end', 'end']
稍加改变:
def addend(lt=[1, 2]):
lt.append('end')
return lt
print(addend())
print(addend())
-------------------------
输出:
[1, 2, 'end']
[1, 2, 'end', 'end']
PyCharm 的提示:
当函数定义中的默认参数赋值为可变对象的时候,PyCharm会自动检测并加以提示,如下所示:
点击“more...” 链接,查看更多详细的描述。
有道词典走起:
Default argument value is mutable less... (Ctrl+F1)
默认参数值是可变的
This inspection detects when a mutable value as list or dictionary is detected in a default value for an argument. Default argument values are evaluated only once at function definition time, which means that modifying the default value of the argument will affect all subsequent calls of the function.
该检查检测何时在参数的默认值中检测到列表或字典等可变值。默认参数值只在函数定义时计算一次,这意味着修改参数的默认值将影响函数的所有后续调用。
说起不可变对象,首当其冲会想到元组(tuple),把它放到默认参数中试试吧:
def addend(lt=(1, 2)):
lt.append('end')
return lt
print(addend())
print(addend())
---------------------------
输出:
Traceback (most recent call last):
File "D:/A00__Dev/pyprojects/untitled1/l2.py", line 10, in
print(addend())
File "D:/A00__Dev/pyprojects/untitled1/l2.py", line 2, in addend
lt.append('end')
AttributeError: 'tuple' object has no attribute 'append'
分析错误原因,依然离不开前面我们得出的结论:
综上,在定义函数默认值参数的时候,其默认值尽量不要使用可变对象,为了防止产生类似问题,做的更彻底些,默认参数值可以直接使用单例的空对象 None 来代替,然后在函数体中判断调用时是否传入了空的参数。
def addend(lt=None):
if lt is None:
lt = []
lt.append('end')
return lt
print(addend())
print(addend())