Python函数:可选参数计算一次?

时间:2014-08-08 12:59:30

标签: python

Python教程4.7.1. Default Argument Values声明如下:

  

重要警告:默认值仅评估一次。这有所不同   当默认值是可变对象时,例如列表,字典或大多数实例   类。例如,以下函数累积传递给它的参数   后续电话:

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)
     

这将打印

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

我不太明白"只评估过一次"在内存管理方面。显然,函数的默认值在首次调用函数时被计算一次,并且即使在函数结束后也存储在单独的存储器地址中。 (根据我的理解,在函数结束后,应该释放所有局部变量?)

我说错了吗?

4 个答案:

答案 0 :(得分:10)

在Python中,函数也是对象,默认值与函数对象一起存储。默认值为本地人;只是在调用函数时,如果没有给出显式值,则参数绑定到默认值。

当Python遇到def <functionname>(<arguments>):语句时,它会为你创建一个函数对象然后;这是'定义时间';该函数不会被调用,而只是被创建。它是然后,在函数对象的属性中评估和存储默认值。

然后,当您调用该函数时,默认值已经已经已创建,并且在您没有为参数提供更具体的值时使用。因为默认值与函数对象一起存储,所以您可以看到函数调用之间对可变对象的更改。

本地人当然仍然被清除,但由于它们是引用(Python中的所有标识符都是),它们绑定的对象只有在没有其他任何引用它们的情况下才被清除。

您可以查看任何函数对象的默认值:

>>> def foo(bar='spam', eggs=[]):
...     eggs.append(bar)
...     return eggs
... 
>>> foo.__defaults__
('spam', [])
>>> foo()
['spam']
>>> foo.__defaults__
('spam', ['spam'])
>>> foo() is foo.__defaults__[1]
True

foo()函数有一个__defaults__属性,当没有传入参数值时要使用的默认值元组。您可以看到可变列表在调用函数时发生更改,并且因为函数返回eggs列表,您还可以看到与该元组中的第二个值完全相同的对象

如果您不希望共享默认值,而是每次调用函数时都需要参数的新值,但未给出参数,则需要将默认值设置为 sentinel 对象。如果您的参数仍设置为函数体中的该sentinel,则可以执行代码来设置新的默认值。 None通常是最佳选择:

def foo(bar='spam', eggs=None):
    if eggs is None:
        eggs = []

如果应该可以使用None作为非默认值,请使用事先创建的单例哨兵:

_sentinel = object()

def foo(bar='spam', eggs=_sentinel):
    if eggs is _sentinel:
        eggs = []

答案 1 :(得分:1)

函数只是python中的一个对象,它是使用def语法创建的。定义函数时,默认值存储在函数对象中,以后不会重新评估它们。

这有时用于创建持续存在于后续调用中的函数变量。您可以使用__defaults__方法检查函数的默认值。

初始化新对象而不是重复使用它的常用方法是:

def f(a, L=None):
    if L is None:
        L = []

    L.append(a)
    return L

您可以查看this page了解详情。

答案 2 :(得分:1)

您定义的函数f本身就是一个对象。定义默认值时,这些默认值将绑定到您创建的函数。

您可以看到这一点:

>>> def f(a, L=[]):
...    L.append(a)
...    return L

>>> print id(f)
4419902952

>>> print f.__defaults__
([],)
>>> f(1)
[1]

>>> print id(f)
4419902952

>>> print f.__defaults__
([1],)

编辑,此外,您可以看到列表容器也不会更改:

>>> print id(f.__defaults__[0])
4419887544
>>> f(2)
[1, 2]
>>> print id(f.__defaults__[0])
4419887544

在每次后续通话中,L功能的默认列表(&#34; f&#34;)都会附加a值。

答案 3 :(得分:0)

对不起,这个答案是针对一个不同的问题,但如果有人想看一下,我会把它留在这里作为参考。定义一次意味着在执行代码的第一点,默认变量被分配给保留在函数对象本身内的对象。

注意只打印1个对象地址,使用默认列表对象。

  def f(a, L=[]):
    print("id default: ", id(L))
    L.append(a)
    print("id used: ", id(L)
    return L

注意打印2个不同的对象地址,当您在函数中执行L = []时,您将L绑定到不同的列表对象,因此默认列表对象不会更改。

def f(a, L=[]):
    print("id default: ", id(L))
    if L == []:
        L = []
    L.append(a)
    print("id used: ", id(L))      
    return L 

上面的函数基本上与下面的函数相同,只是它使用None对象而不是空列表对象。

def f(a, L=None):
    print("id default", id(L))
    if L is None:
        L = []
   L.append(a) 
   print("id used: ", id(L))      
   return L