Python function: Optional argument evaluated once?
python教程4.7.1.默认参数值的状态如下:
Important warning: The default value is evaluated only once. This makes a difference
when the default is a mutable object such as a list, dictionary, or instances of most
classes. For example, the following function accumulates the arguments passed to it on
subsequent calls:
1
2
3
4
5
6
7 def f(a, L=[]):
L.append(a)
return L
print f(1)
print f(2)
print f(3)This will print
1
2
3 [1]
[1, 2]
[1, 2, 3]
号
我不太明白"只评估一次"在内存管理方面的含义。显然,当函数第一次被调用并存储在一个单独的内存地址中时,即使函数已经结束,函数的默认值也会被计算一次。(根据我的理解,在函数结束后,应该释放所有局部变量?)
我说的对吗?
在Python中,函数也是对象,默认值与函数对象一起存储。默认值不是局部变量;只是当调用函数时,参数在没有给定显式值时绑定到默认值。
当python遇到
然后,当您调用函数时,默认值已经创建,并且在您没有为参数提供更具体的值时使用。因为默认值与函数对象一起存储,所以可以看到函数调用之间可变对象的更改。
当然,局部变量仍然被清除,但由于它们是引用(python中的所有标识符都是),因此只有当没有其他对象再引用它们时,才会清除它们绑定到的对象。
您可以查看任意函数对象的默认值:
1 2 3 4 5 6 7 8 9 10 11 12 | >>> 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 |
如果不希望共享默认值,而是每次调用函数时都需要一个新的参数值,但没有给定该参数,则需要将默认值设置为sentinel对象。如果您的参数在函数体中仍然设置为该sentinel,则可以执行代码来设置新的默认值。
1 2 3 | def foo(bar='spam', eggs=None): if eggs is None: eggs = [] |
如果可以使用
1 2 3 4 5 | _sentinel = object() def foo(bar='spam', eggs=_sentinel): if eggs is _sentinel: eggs = [] |
您定义的
您可以看到这一点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | >>> 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],) |
进一步编辑,您可以看到列表容器也不会更改:
1 2 3 4 5 6 | >>> print id(f.__defaults__[0]) 4419887544 >>> f(2) [1, 2] >>> print id(f.__defaults__[0]) 4419887544 |
在随后的每个调用中,您的
函数只是Python中的一个对象,它是使用
这有时用于创建持续到后续调用的函数变量。可以使用
初始化新对象而不是重用新对象的常见方法是:
1 2 3 4 5 6 | def f(a, L=None): if L is None: L = [] L.append(a) return L |
您可以查看此页面了解更多详细信息。
抱歉,这个答案是针对另一个问题的,但如果有人想看,我会把它留在这里作为参考。定义一次意味着在代码执行的第一个点,默认变量被分配给一个对象,这个对象保留在函数对象本身中。
注意,只有1个对象地址被打印,使用默认列表对象。
1 2 3 4 5 | def f(a, L=[]): print("id default:", id(L)) L.append(a) print("id used:", id(L) return L |
注意,打印了两个不同的对象地址,当您在函数内执行l=[]时,您将l绑定到一个不同的列表对象,因此默认的列表对象不会得到更改。
1 2 3 4 5 6 7 | def f(a, L=[]): print("id default:", id(L)) if L == []: L = [] L.append(a) print("id used:", id(L)) return L |
上面的函数与下面的函数基本相同,只是使用了none对象而不是空的list对象。
1 2 3 4 5 6 7 | 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 |