[被坑纪念][转帖]Python函数默认参数的一个小陷阱

来源:互联网 发布:时时彩平台源码出租 编辑:程序博客网 时间:2024/05/22 23:24

原文地址:http://www.byywee.com/page/M0/S211/211243.html

 

Python函数默认参数的一个小陷阱

 

代码
def foo(a1, args = []):
    print "args before = %s" % (args)
    args.insert(0,
 10)
    args.insert(0,
 99999)
    print "args = %s " % (args)

def main():
    foo(
"a")
    foo(
"b")

if __name__ == "__main__":
    main()

以上小程序会有如下输出:

args before = []
args
 = [99999, 10]
args before
 = [99999, 10]
args
 = [99999, 10, 99999, 10]

按照通常的理解,第二次调用的args应该为默认值[],但为什么会变成上一次的结果呢?

查阅Python manual有如下的说法:
    Default parameter values are evaluated when the function definition is executed.This means that the expression is evaluated once, when the function is defined, and that that same “pre-computed” value is used for each call. This is especially important to understand when a default parameter is a mutable object, such as a list or a dictionary: if the function modifies the object (e.g. by appending an item to a list), the default value is in effect modified. This is generally not what was intended. A way around this is to use None as the default, and explicitly test for it in the body of the function, e.g.:


def whats_on_the_telly(penguin=None):
if penguin is None:
penguin
 = []
penguin.append(
"property of the zoo")
return penguin

至此,原因已经很清楚了:函数中的参数默认值是一个可变的list, 函数体内修改了原来的默认值,而python会将修改后的值一直保留,并作为下次函数调用时的参数默认值

 

————————————————————————————————————————————————

 

遥遥补充:

    不仅如此,在class里,这个情况可能会发生的更隐蔽。

class edit_unit:
    # WARNING: 不要用list和dict等对象做初始化,否则会在无意中修改默认值。

    def __init__(self, valids=data_valid(), childs=[]):
       self.childs = childs[:]  #如果这里不拷贝,对self.childs的操作就变成了对childs参数默认值的操作

    如果这里做了切片也不好,会影响list托管和优化。

    可以考虑childs默认参数为None,一开始就判断

    if childs==None:

        childs=[]

    这样就保护了默认参数绝对没问题。但是话说回来,如果让self.childs直接等于传进来的childs,还是得注意引用的问题。如果这个原始list不会被修改还好,一旦修改又是个坑。

    总之脚本语言务必注意list、dict这种引用类型

原创粉丝点击