python函数默认参数只评估一次?

Fen*_*ang 30 python parameters function

我是一个python初学者,阅读'python tutorial',它说如果我们有一个函数:

def f(a, L=[]):
     L.append(a)
     return L
print f(1)
print f(2)
print f(3)
Run Code Online (Sandbox Code Playgroud)

这将打印

[1]
[1, 2]
[1, 2, 3]
Run Code Online (Sandbox Code Playgroud)

因为默认值只计算一次而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)
Run Code Online (Sandbox Code Playgroud)

这将输出:

[1]
[2]
[3]
Run Code Online (Sandbox Code Playgroud)

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

Bre*_*out 20

Python通过值将参数传递给函数; 因此,对于对象,传递的值是对对象的引用,而不是对象的新副本.

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

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

把它们放在一起:

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

但是,由于None是一个不可变的内置类型,默认情况下的"预先计算"值None就是这样.所以None每次调用函数时都会有参数.

希望这有帮助!我认为教程可能有更好的措辞,因为我最初也对此感到困惑.

  • 与最高票数相比,这对我来说更清楚. (2认同)

小智 15

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

  • 我认为 Python 团队这不是一个好的决定!例如,我们有一个参数的默认值为“datetime.now()”。这导致默认参数是应用程序启动时间,而不是现在(),这确实令人困惑。有些语言有时可能会让开发人员感到惊讶,但 python 通常不会这样做。 (2认同)

Dou*_* T. 3

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

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

  • 起初 L 引用 None,它在第一次调用时重新指向一个新的空列表,而不是每次调用,因为一次之后,L 不是 None 并且 if 语句无法计算。 (2认同)