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