rank | vote | view | answer | url |
---|---|---|---|---|
26 | 2114 | 118677 | 30 | url |
Python中的"小震撼":变化的默认参数
许多 Python 老手也被下面的问题困扰:
def foo(a=[]):
a.append(5)
return a
Python 新手估计可能会想这个函数返回一个只有元素[5]
的列表.但是结果却出人意料:
>>> foo()
[5]
>>> foo()
[5, 5]
>>> foo()
[5, 5, 5]
>>> foo()
[5, 5, 5, 5]
>>> foo()
我的一个经理曾经碰到过这个特性并把它叫做语言的"动态设计缺陷". 这个现象应当有更深层次的解释, 如果你不懂它的内部它确实非常令人困惑. 然而我不能回答下面的问题:是什么原因使默认参数在函数的定义时被绑定,而不是在执行时?我怀疑这个特性在现实中有没有实际的用途(就像在C语言中有谁去用静态变量?)
事实上这并不是设计缺陷,也不是什么内部或性能原因.
原因很简单,Python中的函数是最高等级的对象,而不仅仅是一小段代码.
试着这么来理解:一个函数是一个被它自己定义而执行的对象;默认参数是一种"成员数据",所以它们的状态和其他对象一样,会随着每一次调用而改变.
Effbot在它的Python中的默认参数对这种行为的原因解释的非常清楚!我强烈建议你读一读能对函数对象的工作原理有更深一步的了解.
Python 中的默认参数(文章翻译)
Python 默认参数很容易让新手犯错.
引起错误的原因通常是使用可变(mutable)对象作为了参数的默认值, 这个可变对象是可以被修改的, 比如说列表(list)和字典(dict)
例如:
>>> def function(data=[]):
... data.append(1)
... return data
...
>>> function()
[1]
>>> function()
[1, 1]
>>> function()
[1, 1, 1]
可以看到上面的列表(list)越来越长, 如果检查这个列表的id, 会发现他返回的是同一个值:
>>> id(function())
12516768
>>> id(function())
12516768
>>> id(function())
12516768
原因很简单: 在每次函数调用的时候都使用同一个对象(object), 这个可修改的特性我们叫做 "sticky".
原理
只有当"def"语句执行的时候才会对默认参数值进行赋值, 可以看看文档的定义:
https://docs.python.org/2.0/ref/function.html
还要注意的是在Python 中 "def" 是可执行语句, 默认参数仅在"def" 语句环境里生效.每次执行"def"语句都会创建一个新的函数对象(每次都会对默认参数进行赋值)