in python

python中函数参数的默认值和List, dict

Python 的函数定义中,有种带有默认值的参数的语法,例如:

def foo( p = [] ): 
    print p

如果我们调用此函数的时候,没有传入参数 p ,那 p 就用默认值。

接下来,我们看看下面这段代码会得到什么结果:

def foo( p = [] ):
    p.append('a')
    print p

foo()
foo()

最开始,直觉告诉我,会得到结果:

['a']
['a']

但是实际结果是:

['a']
['a','a']

原来对于这种形式定义的带默认值的参数,参数的默认值是在函数定义的时候初始化的,当我们使用了 mutable 的对象的时候,我们中途改变了这个对象,在后面的函数调用中,它就不再是写在代码里的那个默认值。显然这样是很混乱的,我们不应该这样做。
python 官方文档也解释了这个事情,并且给出了解决方案,当我们需要一个 mutable 的对象作为默认值的时候,我们可以这样做:

def foo( p = None ):
    if p is None:
        p = []
    p.append('a')
    print p

这样每次都是生成一个新的,就没问题了。为了证实,带默认值的参数确实是在函数定义的时候初始化的,我们可以使用 id(p) 这个函数来查看这个变量的 identity , 可以看到,如果不传参数,让函数使用默认值,每次的 id 都是一样的。

  1. 这种坑有点坏,很容易犯。刚刚试了下,ruby没这个问题,会默认生成新的对象。
    ===========
    def foo(p = [])
    p << 'a'
    puts "p is #{p}, objectId is : #{p.object_id}"
    end

    foo
    foo [9, 'hello']
    foo

  2. python的list,比如A=B,改变了B,就相当于改变了A,因为地址映射是一样的。
    这里是不是也是这个原因?我感觉是如此。

    • 是的。但是必须是 A 和 B 保存的是一个 mutable 的值,如果不是,比如 string , number , tuple 这些,你就没办法改变保存的值本身,只能重新生成一个对象,将 B 重新指向新的地址。这里最重要的问题是,对于这种方式定义的函数,每次不会生成新的默认值。

        • 其他语言也有很多是这样的,比如 javascript 。对于 array , object 这些类型,对变量赋值,都是赋的他们的引用。