1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
In [29]: def f(x,l=[]): ...: for i in range(x): ...: l.append(i) ...: print(l) ...: In [30]: f(2) [0, 1] In [31]: f(3,[3,2,1]) [3, 2, 1, 0, 1, 2] In [32]: f(3) [0, 1, 0, 1, 2] In [33]: dir(f) Out[33]: ['__annotations__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] In [34]: f.__defaults__ Out[34]: ([0, 1, 0, 1, 2],) In [35]: |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
def f(x,l=[]): for z in range(x): l.append(z) print(l) if __name__ == '__main__': print(f.__defaults__) f(3) print(f.__defaults__) f(3,["a",'b']) print(f.__defaults__) f(3) print(f.__defaults__) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
有网友问 >>> def foo(bar=[]): ... bar.append("baz") ... return bar >>> foo() ["baz"] >>> foo() ["baz", "baz"] >>> foo() ["baz", "baz", "baz"] 为什么多次调用foo()bar的值不会重新赋值为[]呢?bar的作用域不是应该在foo函数之内吗? 文档中是这么说的。 |
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 |
i = 5 def f(arg=i): print(arg) i = 6 f() |
输出值为5,不是6
例子二:
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)) |
输出值为
[1]
[1, 2]
[1, 2, 3]
这个原因是由于默认参数只计算一次,因为list 是可变数据类型,函数每次调用时,L 是同一个对象的引用。
加一个id(),可以判断每次函数的调用都是访问的同一个list 实例对象。
1 2 3 4 5 6 7 8 9 |
def f(a, L=[]): L.append(a) print(id(L)) return L print(f(1)) print(f(2)) print(f(3)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
先定义一个函数,传入一个list,添加一个END再返回: def add_end(L=[]): L.append('END') return L 当你正常调用时,结果似乎不错: >>> add_end([1, 2, 3]) [1, 2, 3, 'END'] >>> add_end(['x', 'y', 'z']) ['x', 'y', 'z', 'END'] 当你使用默认参数调用时,一开始结果也是对的: >>> add_end() ['END'] 但是,再次调用add_end()时,结果就不对了: >>> add_end() ['END', 'END'] >>> add_end() ['END', 'END', 'END'] 很多初学者很疑惑,默认参数是[],但是函数似乎每次都“记住了”上次添加了'END'后的list。 原因解释如下: Python函数在定义的时候,默认参数L的值就被计算出来了,即[],因为默认参数L也是一个变量,它指向对象[],每次调用该函数,如果改变了L的内容,则下次调用时,默认参数的内容就变了,不再是函数定义时的[]了。 所以,定义默认参数要牢记一点:默认参数必须指向不变对象! 要修改上面的例子,我们可以用None这个不变对象来实现: def add_end(L=None): if L is None: L = [] L.append('END') return L 现在,无论调用多少次,都不会有问题: >>> add_end() ['END'] >>> add_end() ['END'] 为什么要设计str、None这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。 |
