47

我是一个 python 初学者,正在阅读“python 教程”,它说我们是否有一个函数:

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

这将打印

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

因为默认值只计算一次,并且 list 是一个可变对象。我能理解。

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

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]

但是为什么?这要怎么解释。我们知道默认值仅被评估once,当我们调用 f(2) 时,L 不是 None 并且if(在第 2 行)不能为真,所以 L.append(a) == [1, 2]。我是否可以猜测由于某种原因再次评估默认值,但什么是“某种原因”,只是因为 python 解释器看到if L is None: L = []

4

5 回答 5

36

Python按值传递参数给函数;所以对于对象,传递的值是对对象的引用,而不是对象的新副本。

这与官方文档的以下部分一起帮助我更好地理解它(强调我的):

执行函数定义时会评估默认参数值 [...]。这意味着在定义函数时对表达式求值一次,并且每次调用都使用相同的“预计算”值。当默认参数是可变对象(例如列表或字典)时,这一点尤其重要:如果函数修改了对象(例如,通过将项目附加到列表中),则默认值实际上已被修改。[...] 解决这个问题的一种方法是使用 None 作为默认值,并在函数体中显式测试它 [...]

把它们放在一起:

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

但是,由于None是不可变的内置类型,因此默认的“预计算”值None就是这样。None因此,每次调用该函数时,该参数都会出现。

希望这会有所帮助!我确实认为本教程可以有更好的措辞,因为一开始我也对此感到困惑。

于 2015-11-06T18:25:05.313 回答
19

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

于 2012-10-26T13:03:25.973 回答
3

在您的第二个示例中,您有一个变量L。起初LNone. 您在每次调用时将其重新指向一个新的空列表,然后改变该新列表。记住L = []是一样的L = list()

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

于 2012-10-26T12:59:13.797 回答
3

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

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

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

希望这是有道理的。

于 2014-06-05T07:07:29.843 回答
2

发生的情况如下:

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

默认参数仅在函数定义时评估一次。这将创建一个闭包。将闭包视为定义函数的函数代码+环境。

因此,在这种情况下,当定义函数时,将L其分配给[],现在对该函数的每个后续调用都将使用L.

该教程还提到:

默认值在定义范围内的函数定义点进行评估(并且定义范围与函数代码一起是闭包的一部分)

http://docs.python.org/2/tutorial/controlflow.html#default-argument-values

于 2013-12-31T20:00:07.923 回答