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
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
]
:
|
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
|
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__
)
|
有网友问 >>> def foo(bar=[]): ... bar.append("baz") ... return bar >>> foo() ["baz"] >>> foo() ["baz", "baz"] >>> foo() ["baz", "baz", "baz"] 为什么多次调用foo()bar的值不会重新赋值为[]呢?bar的作用域不是应该在foo函数之内吗? 文档中是这么说的。
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:
重要警告:默认值只计算一次。当默认值是可变对象(如列表,字典或大多数类的实例)时,这会有所不同。例如,以下函数会累积在后续调用中传递给它的参数
例子一:
i = 5 def f(arg=i): print(arg) i = 6 f()
1
2
3
4
5
6
7
|
i
=
5
def
f
(
arg
=
i
)
:
print
(
arg
)
i
=
6
f
(
)
|
输出值为5,不是6
例子二:
def f(a, L=[]): L.append(a) return L print(f(1)) print(f(2)) print(f(3))
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 实例对象。
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
|
def
f
(
a
,
L
=
[
]
)
:
L
.
append
(
a
)
print
(
id
(
L
)
)
return
L
print
(
f
(
1
)
)
print
(
f
(
2
)
)
print
(
f
(
3
)
)
|
先定义一个函数,传入一个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这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
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这样的不变对象呢?因为不变对象一旦创建,对象内部的数据就不能修改,这样就减少了由于修改数据导致的错误。此外,由于对象不变,多任务环境下同时读取对象不需要加锁,同时读一点问题都没有。我们在编写程序时,如果可以设计一个不变对象,那就尽量设计成不变对象。
|