关于python:将列表作为默认函数参数的奇怪行为

Strange behavior with a list as a default function argument

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
"Least Astonishment" in Python: The Mutable Default Argument
List extending strange behaviour
Pyramid traversal view lookup using method names

假设我有这个功能:

1
2
3
def a(b=[]):
    b += [1]
    print b

调用它会产生以下结果:

1
2
3
4
5
6
>>> a()
[1]
>>> a()
[1, 1]
>>> a()
[1, 1, 1]

当我把b += [1]改为b = b + [1]时,函数的行为改变:

1
2
3
4
5
6
>>> a()
[1]
>>> a()
[1]
>>> a()
[1]

b = b + [1]b += [1]有何区别?为什么会这样?


在python中,不能保证a += ba = a + b执行相同的操作。

对于列表,someList += otherList在适当位置修改someList,基本相当于someList.extend(otherList),然后将名称someList重新绑定到同一列表。另一方面,someList = someList + otherList通过连接两个列表来构造一个新列表,并将名称someList绑定到该新列表。

这意味着,对于+=,名称最终指向它已经指向的同一个对象,而对于+,它指向一个新对象。由于函数默认值只计算一次(见这一引人深思的问题),这意味着使用+=操作会堆积起来,因为它们都修改了相同的原始对象(默认参数)。


b += [1]改变了函数默认值(导致最不令人惊讶的FAQ)。b = b + [1]采用默认参数b—用+ [1]创建一个新的列表,并将其绑定到b上。一个改变列表,另一个创建新列表。


定义函数时

1
2
3
>>> def a(b=[]):
    b += [1]
    return b

它将所有默认参数保存在一个特殊的位置。它可以通过以下方式进行实际访问:

1
2
>>> a.func_defaults
([],)

第一个默认值是一个list具有以下ID:

1
2
>>> id(a.func_defaults[0])
15182184

让我们尝试调用函数:

1
2
3
4
>>> print a()
[1]
>>> print a()
[1, 1]

并查看返回值的ID:

1
2
3
4
>>> print id(a())
15182184
>>> print id(a())
15182184

如您所见,它与第一个默认值列表的ID相同。

该函数的不同输出是由b+=...修改b的位置而不是创建新的列表这一事实来解释的。而b是保存在缺省值元组中的列表。因此,对列表的所有更改都保存在那里,并且函数的每次调用都使用不同的b值。