python函数默认参数只计算一次?

python function default parameter is evaluated only once?

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

我是一个Python初学者,正在阅读"Python教程",它说如果我们有一个函数:

1
2
3
4
5
6
def f(a, L=[]):
     L.append(a)
     return L
print f(1)
print f(2)
print f(3)

这将打印

1
2
3
[1]
[1, 2]
[1, 2, 3]

因为默认值只计算一次,而列表是可变对象。我能理解。

它说继续,如果我们不希望在子后续调用之间共享默认值,我们可以:

1
2
3
4
5
6
7
8
def f(a, L=None):
   if L is None:           #line  2
       L = []            
   L.append(a)
   return L
print f(1)            
print f(2)
print f(3)

这将输出:

1
2
3
[1]
[2]
[3]

但是为什么呢?如何解释这一点。我们知道默认值只计算once,当我们调用f(2)时,l不是无,if在第2行不能为真,所以l.append(a)==[1,2]。我能猜出默认值是出于某种原因被重新计算的吗?但什么是"某种原因",仅仅是因为python解释器看到了if L is None: L = []


python按值将参数传递给函数;因此对于对象,传递的值是对对象的引用,而不是对象的新副本。

这一点,连同以下部分的官方文件,帮助我更好地理解它(强调我的):

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 the 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. [...] A way around this is to use None as the default, and explicitly test for it in the body of the function [...]

把它们放在一起:

如果将参数的默认值定义为可变对象(如[]),则"预计算"值是对该对象的引用,因此对函数的每个调用都将始终引用同一对象,然后可以在函数的多个调用中对该对象进行变异。

但是,由于None是不可变的内置类型,因此默认None的"预计算"值就是这样。所以每次调用函数时,参数都是None

希望有帮助!我确实认为教程应该有更好的措辞,因为一开始我也被它搞糊涂了。


"默认值只计算一次"并不意味着具有默认值的参数在函数调用之间保留其值。这意味着,您指定的表达式(def f(a, L=None)None部分)只计算一次,它产生的对象存储在一个隐藏位置,如果调用时没有给出该参数的值,则重新使用。每次调用时,参数仍然重置为值(默认值或非默认值)。


我认为这是因为列表是可变对象,而值"无"是不可变的。

对于第一个函数,变量L在函数环境之外(在函数定义中),它引用一个空列表。然后在函数环境中对此列表进行更改,但是由于列表是可变的,因此函数环境之外的变量l引用了这个现在变了的列表,并且每次调用函数时,更改都会传播。

对于第二个函数,变量l也在函数环境之外(在函数定义中),但这次它指的是无,这是不可变的。现在,您在函数环境中所做的每一个更改都不会影响函数环境之外的l所指的内容。函数环境中的变量l在您更改时引用了不同的内容。首先,它引用一个空列表,然后是一个附加值的列表。然后返回此列表。下次调用函数时,使用函数环境之外的变量l来调用它,该变量没有更改,但仍然引用none。

希望这是有道理的。


在第二个示例中,您有一个变量L。起初,LNone。您在每次调用时将它重新插入到一个新的空列表中,然后改变这个新列表。记住,L = []L = list()相同。

然而,在第一个示例中,l在函数声明处设置为新列表一次。每次调用函数时,L都不会重置为[]。所以你总是在改变同一个列表。


发生的情况如下:

当调用python函数时,它是在定义它的环境中进行评估的,而不是在调用它的环境中进行评估的,尽管第二部分是次要的(没有双关语),以回答您的问题。

默认参数在函数定义时只计算一次。这将创建一个闭包。把闭包看作函数代码+定义函数的环境。

因此,在定义函数时,L被分配给[],现在对函数的每个后续调用都将使用该值L

本教程还提到:

默认值在定义范围内的函数定义点进行计算(并且定义范围是闭包的一部分,以及函数代码)。

http://docs.python.org/2/tutorial/controlflow.html默认参数值